diff --git a/krita/data/aboutdata/credits.txt b/krita/data/aboutdata/credits.txt index 5b9efe7042..82ec941b42 100644 --- a/krita/data/aboutdata/credits.txt +++ b/krita/data/aboutdata/credits.txt @@ -1,8 +1,10 @@ Peter Sikking:Project Vision Ramon Miranda:Artist, Muses author, brush and preset creator David Revoy:Brushes and Palettes Ilya Portnov:MyPaint shade selector Martin Renold:MyPaint shade selector Saisho Kazuki:Japanese Animation template Pasquale D'Antuono:Donor +David Gowers:Dither patterns + diff --git a/krita/data/patterns/CMakeLists.txt b/krita/data/patterns/CMakeLists.txt index 09fb559736..6f5917648f 100644 --- a/krita/data/patterns/CMakeLists.txt +++ b/krita/data/patterns/CMakeLists.txt @@ -1,86 +1,117 @@ ########### install files ############### install(FILES Abstract_lines.png 01_canvas.png 02_rough-canvas.png 02b_WoofTissue.png 03_default-paper.png 04_paper-C-grain.png 05_paper-torchon.png 06_hard-grain.png 08_bump-relief.png 09_drawed_crosshatched.png 09b_drawed-CrossedLines.png 10_drawed_dotted.png 11_drawed_furry.png 12_drawed_vertical.png 13_drawed_swirl.png 14_texture-rock.png 15_texture-rockB.png 16_texture-woody.png 17_texture-melt.png 18_texture-bark.png 18b_WaveFlex.png 19_texture-crackle.png 20_texture-vegetal.png 21_texture-chainmail.png 22_texture-reptile.png 23-dynamic-screentone-A.png 24-dynamic-screentone-B.png 25-dynamic-screentone-C.png 26_brush-marks.png Cross01.pat Cross02.pat Cross03.pat Cross04.pat Cross05.pat Cross06.pat Cross07.pat Crumpled_Paper.pat Grid01.pat Grid02.pat Grid03.pat Grid04.pat Grid05.pat HR_PastelPaper_02.pat HR_Wall_Paper.pat Maze_lines.png Pattern01.pat Pattern02.pat Pattern03.pat Pattern04.pat Pattern05.pat Pattern06.pat Rough_Paper.png Rough_Wall_With_Impasto.png Sand_fine.png Stars_Sized.png Squares01.pat Squares02.pat Squares03.pat Squares04.pat Squares05.pat Squares06.pat Squares07.pat Squares08.pat Squares09.pat Squares10.pat Stripes02.pat Stripes03.pat Stripes04.pat Stripes05.pat Stripes06.pat Stripes07.pat Stripes08.pat Stripes09.pat Zigzag01.pat Zigzag02.pat Zigzag03.pat Zigzag04.pat fractal_pattern.pat generic_paper1.pat generic_paper2.pat hexacolBW__2.pat +DITH_0202_CLUS.pat +DITH_0202_GEN_.pat +DITH_0202_HORZ.pat +DITH_0202_VERT.pat +DITH_0404_ALT_.pat +DITH_0404_BL22.pat +DITH_0404_CLUS.pat +DITH_0404_CURL.pat +DITH_0404_DIAG.pat +DITH_0404_ELL2.pat +DITH_0404_ELL3.pat +DITH_0404_ELLS.pat +DITH_0404_GEN_.pat +DITH_0404_HORZ.pat +DITH_0404_SHUR.pat +DITH_0404_SLIC.pat +DITH_0404_VERT.pat +DITH_0404_WAV2.pat +DITH_0404_WAVE.pat +DITH_0404_ZORO.pat +DITH_0808_BL22.pat +DITH_0808_BL22_v.pat +DITH_0808_BUBL.pat +DITH_0808_CIRC.pat +DITH_0808_CLUS.pat +DITH_0808_DIAM.pat +DITH_0808_PANL.pat +DITH_0808_SPOT.pat +DITH_0808_SWRL.pat +DITH_0808_WAVE.pat +DITH_3232_CSTR.pat DESTINATION ${DATA_INSTALL_DIR}/krita/patterns) diff --git a/krita/data/patterns/DITH_0202_CLUS.pat b/krita/data/patterns/DITH_0202_CLUS.pat new file mode 100644 index 0000000000..a490dd26ed Binary files /dev/null and b/krita/data/patterns/DITH_0202_CLUS.pat differ diff --git a/krita/data/patterns/DITH_0202_GEN_.pat b/krita/data/patterns/DITH_0202_GEN_.pat new file mode 100644 index 0000000000..1957ce2b67 Binary files /dev/null and b/krita/data/patterns/DITH_0202_GEN_.pat differ diff --git a/krita/data/patterns/DITH_0202_HORZ.pat b/krita/data/patterns/DITH_0202_HORZ.pat new file mode 100644 index 0000000000..2168049001 Binary files /dev/null and b/krita/data/patterns/DITH_0202_HORZ.pat differ diff --git a/krita/data/patterns/DITH_0202_VERT.pat b/krita/data/patterns/DITH_0202_VERT.pat new file mode 100644 index 0000000000..7fcf9acac4 Binary files /dev/null and b/krita/data/patterns/DITH_0202_VERT.pat differ diff --git a/krita/data/patterns/DITH_0404_ALT_.pat b/krita/data/patterns/DITH_0404_ALT_.pat new file mode 100644 index 0000000000..03acba8794 Binary files /dev/null and b/krita/data/patterns/DITH_0404_ALT_.pat differ diff --git a/krita/data/patterns/DITH_0404_BL22.pat b/krita/data/patterns/DITH_0404_BL22.pat new file mode 100644 index 0000000000..8862a86b53 Binary files /dev/null and b/krita/data/patterns/DITH_0404_BL22.pat differ diff --git a/krita/data/patterns/DITH_0404_CLUS.pat b/krita/data/patterns/DITH_0404_CLUS.pat new file mode 100644 index 0000000000..2ee2d9e7a4 Binary files /dev/null and b/krita/data/patterns/DITH_0404_CLUS.pat differ diff --git a/krita/data/patterns/DITH_0404_CURL.pat b/krita/data/patterns/DITH_0404_CURL.pat new file mode 100644 index 0000000000..0ba3bd0570 Binary files /dev/null and b/krita/data/patterns/DITH_0404_CURL.pat differ diff --git a/krita/data/patterns/DITH_0404_DIAG.pat b/krita/data/patterns/DITH_0404_DIAG.pat new file mode 100644 index 0000000000..23d6efa933 Binary files /dev/null and b/krita/data/patterns/DITH_0404_DIAG.pat differ diff --git a/krita/data/patterns/DITH_0404_ELL2.pat b/krita/data/patterns/DITH_0404_ELL2.pat new file mode 100644 index 0000000000..72adb0a39e Binary files /dev/null and b/krita/data/patterns/DITH_0404_ELL2.pat differ diff --git a/krita/data/patterns/DITH_0404_ELL3.pat b/krita/data/patterns/DITH_0404_ELL3.pat new file mode 100644 index 0000000000..502ed21949 Binary files /dev/null and b/krita/data/patterns/DITH_0404_ELL3.pat differ diff --git a/krita/data/patterns/DITH_0404_ELLS.pat b/krita/data/patterns/DITH_0404_ELLS.pat new file mode 100644 index 0000000000..0fa32df133 Binary files /dev/null and b/krita/data/patterns/DITH_0404_ELLS.pat differ diff --git a/krita/data/patterns/DITH_0404_GEN_.pat b/krita/data/patterns/DITH_0404_GEN_.pat new file mode 100644 index 0000000000..7dfe88bbdc Binary files /dev/null and b/krita/data/patterns/DITH_0404_GEN_.pat differ diff --git a/krita/data/patterns/DITH_0404_HORZ.pat b/krita/data/patterns/DITH_0404_HORZ.pat new file mode 100644 index 0000000000..de52f84159 Binary files /dev/null and b/krita/data/patterns/DITH_0404_HORZ.pat differ diff --git a/krita/data/patterns/DITH_0404_SHUR.pat b/krita/data/patterns/DITH_0404_SHUR.pat new file mode 100644 index 0000000000..6f355da51c Binary files /dev/null and b/krita/data/patterns/DITH_0404_SHUR.pat differ diff --git a/krita/data/patterns/DITH_0404_SLIC.pat b/krita/data/patterns/DITH_0404_SLIC.pat new file mode 100644 index 0000000000..73f5c74cd1 Binary files /dev/null and b/krita/data/patterns/DITH_0404_SLIC.pat differ diff --git a/krita/data/patterns/DITH_0404_VERT.pat b/krita/data/patterns/DITH_0404_VERT.pat new file mode 100644 index 0000000000..d086e36830 Binary files /dev/null and b/krita/data/patterns/DITH_0404_VERT.pat differ diff --git a/krita/data/patterns/DITH_0404_WAV2.pat b/krita/data/patterns/DITH_0404_WAV2.pat new file mode 100644 index 0000000000..a01d844e7b Binary files /dev/null and b/krita/data/patterns/DITH_0404_WAV2.pat differ diff --git a/krita/data/patterns/DITH_0404_WAVE.pat b/krita/data/patterns/DITH_0404_WAVE.pat new file mode 100644 index 0000000000..cd9b5721a3 Binary files /dev/null and b/krita/data/patterns/DITH_0404_WAVE.pat differ diff --git a/krita/data/patterns/DITH_0404_ZORO.pat b/krita/data/patterns/DITH_0404_ZORO.pat new file mode 100644 index 0000000000..919002432d Binary files /dev/null and b/krita/data/patterns/DITH_0404_ZORO.pat differ diff --git a/krita/data/patterns/DITH_0808_BL22.pat b/krita/data/patterns/DITH_0808_BL22.pat new file mode 100644 index 0000000000..2683b8a2f5 Binary files /dev/null and b/krita/data/patterns/DITH_0808_BL22.pat differ diff --git a/krita/data/patterns/DITH_0808_BL22_v.pat b/krita/data/patterns/DITH_0808_BL22_v.pat new file mode 100644 index 0000000000..ed86f75bf8 Binary files /dev/null and b/krita/data/patterns/DITH_0808_BL22_v.pat differ diff --git a/krita/data/patterns/DITH_0808_BUBL.pat b/krita/data/patterns/DITH_0808_BUBL.pat new file mode 100644 index 0000000000..cfe3d45ac9 Binary files /dev/null and b/krita/data/patterns/DITH_0808_BUBL.pat differ diff --git a/krita/data/patterns/DITH_0808_CIRC.pat b/krita/data/patterns/DITH_0808_CIRC.pat new file mode 100644 index 0000000000..b4d40d4650 Binary files /dev/null and b/krita/data/patterns/DITH_0808_CIRC.pat differ diff --git a/krita/data/patterns/DITH_0808_CLUS.pat b/krita/data/patterns/DITH_0808_CLUS.pat new file mode 100644 index 0000000000..a0c41af628 Binary files /dev/null and b/krita/data/patterns/DITH_0808_CLUS.pat differ diff --git a/krita/data/patterns/DITH_0808_DIAM.pat b/krita/data/patterns/DITH_0808_DIAM.pat new file mode 100644 index 0000000000..3e72373753 Binary files /dev/null and b/krita/data/patterns/DITH_0808_DIAM.pat differ diff --git a/krita/data/patterns/DITH_0808_PANL.pat b/krita/data/patterns/DITH_0808_PANL.pat new file mode 100644 index 0000000000..e84dfddede Binary files /dev/null and b/krita/data/patterns/DITH_0808_PANL.pat differ diff --git a/krita/data/patterns/DITH_0808_SPOT.pat b/krita/data/patterns/DITH_0808_SPOT.pat new file mode 100644 index 0000000000..7eddc95cae Binary files /dev/null and b/krita/data/patterns/DITH_0808_SPOT.pat differ diff --git a/krita/data/patterns/DITH_0808_SWRL.pat b/krita/data/patterns/DITH_0808_SWRL.pat new file mode 100644 index 0000000000..3a9abbdc12 Binary files /dev/null and b/krita/data/patterns/DITH_0808_SWRL.pat differ diff --git a/krita/data/patterns/DITH_0808_WAVE.pat b/krita/data/patterns/DITH_0808_WAVE.pat new file mode 100644 index 0000000000..f889ade6af Binary files /dev/null and b/krita/data/patterns/DITH_0808_WAVE.pat differ diff --git a/krita/data/patterns/DITH_3232_CSTR.pat b/krita/data/patterns/DITH_3232_CSTR.pat new file mode 100644 index 0000000000..595932a911 Binary files /dev/null and b/krita/data/patterns/DITH_3232_CSTR.pat differ diff --git a/krita/data/patterns/dith_license.txt b/krita/data/patterns/dith_license.txt new file mode 100644 index 0000000000..05a4d72068 --- /dev/null +++ b/krita/data/patterns/dith_license.txt @@ -0,0 +1,465 @@ +"dithering/halftoning patterns" by David Gowers + +Files: + +DITH_0202_CLUS.pat +DITH_0202_GEN_.pat +DITH_0202_HORZ.pat +DITH_0202_VERT.pat +DITH_0404_ALT_.pat +DITH_0404_BL22.pat +DITH_0404_CLUS.pat +DITH_0404_CURL.pat +DITH_0404_DIAG.pat +DITH_0404_ELL2.pat +DITH_0404_ELL3.pat +DITH_0404_ELLS.pat +DITH_0404_GEN_.pat +DITH_0404_HORZ.pat +DITH_0404_SHUR.pat +DITH_0404_SLIC.pat +DITH_0404_VERT.pat +DITH_0404_WAV2.pat +DITH_0404_WAVE.pat +DITH_0404_ZORO.pat +DITH_0808_BL22.pat +DITH_0808_BL22_v.pat +DITH_0808_BUBL.pat +DITH_0808_CIRC.pat +DITH_0808_CLUS.pat +DITH_0808_DIAM.pat +DITH_0808_PANL.pat +DITH_0808_SPOT.pat +DITH_0808_SWRL.pat +DITH_0808_WAVE.pat +DITH_3232_CSTR.pat + +Licensed under CC-BY-SA: + +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/krita/main.cc b/krita/main.cc index eb3351d799..fbdfd20f11 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,558 +1,560 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2015 Boudewijn Rempt * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050900 #include #endif #include #include #include #include #include #include "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "data/splash/splash_screen_x2.xpm" #include "data/splash/splash_holidays_x2.xpm" #include "KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" #include "KisApplicationArguments.h" #include #include "input/KisQtWidgetsTweaker.h" #include #include #if defined Q_OS_WIN #include "config_use_qt_tablet_windows.h" #include #ifndef USE_QT_TABLET_WINDOWS #include #include #else #include #endif #include "config-high-dpi-scale-factor-rounding-policy.h" #include "config-set-has-border-in-full-screen-default.h" #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT #include #endif #include #endif #if defined HAVE_KCRASH #include #elif defined USE_DRMINGW namespace { void tryInitDrMingw() { wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit()); } } // namespace #endif #ifdef Q_OS_WIN namespace { typedef enum ORIENTATION_PREFERENCE { ORIENTATION_PREFERENCE_NONE = 0x0, ORIENTATION_PREFERENCE_LANDSCAPE = 0x1, ORIENTATION_PREFERENCE_PORTRAIT = 0x2, ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = 0x4, ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = 0x8 } ORIENTATION_PREFERENCE; typedef BOOL WINAPI (*pSetDisplayAutoRotationPreferences_t)( ORIENTATION_PREFERENCE orientation ); void resetRotation() { QLibrary user32Lib("user32"); if (!user32Lib.load()) { qWarning() << "Failed to load user32.dll! This really should not happen."; return; } pSetDisplayAutoRotationPreferences_t pSetDisplayAutoRotationPreferences = reinterpret_cast(user32Lib.resolve("SetDisplayAutoRotationPreferences")); if (!pSetDisplayAutoRotationPreferences) { dbgKrita << "Failed to load function SetDisplayAutoRotationPreferences"; return; } bool result = pSetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE); dbgKrita << "SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE_NONE) returned" << result; } } // namespace #endif extern "C" int main(int argc, char **argv) { // The global initialization of the random generator qsrand(time(0)); bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); #if defined HAVE_X11 qputenv("QT_QPA_PLATFORM", "xcb"); #endif // Workaround a bug in QNetworkManager qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1)); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita4" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); #if QT_VERSION >= 0x050900 QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache, true); #endif #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY // This rounding policy depends on a series of patches to Qt related to // https://bugreports.qt.io/browse/QTBUG-53022. These patches are applied // in ext_qt for WIndows (patches 0031-0036). // // The rounding policy can be set externally by setting the environment // variable `QT_SCALE_FACTOR_ROUNDING_POLICY` to one of the following: // Round: Round up for .5 and above. // Ceil: Always round up. // Floor: Always round down. // RoundPreferFloor: Round up for .75 and above. // PassThrough: Don't round. // // The default is set to RoundPreferFloor for better behaviour than before, // but can be overridden by the above environment variable. QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); #endif const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); bool singleApplication = true; bool enableOpenGLDebug = false; bool openGLDebugSynchronous = false; bool logUsage = true; { singleApplication = kritarc.value("EnableSingleApplication", true).toBool(); if (kritarc.value("EnableHiDPI", true).toBool()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } if (!qgetenv("KRITA_HIDPI").isEmpty()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } #ifdef HAVE_HIGH_DPI_SCALE_FACTOR_ROUNDING_POLICY if (kritarc.value("EnableHiDPIFractionalScaling", true).toBool()) { QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); } #endif if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) { enableOpenGLDebug = true; } else { enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool(); } if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) { openGLDebugSynchronous = true; } KisConfig::RootSurfaceFormat rootSurfaceFormat = KisConfig::rootSurfaceFormat(&kritarc); KisOpenGL::OpenGLRenderer preferredRenderer = KisOpenGL::RendererAuto; logUsage = kritarc.value("LogUsage", true).toBool(); - const QString preferredRendererString = kritarc.value("OpenGLRenderer", "auto").toString(); - preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString); - #ifdef Q_OS_WIN // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP // might get weird crashes atm. + const QString preferredRendererString = kritarc.value("OpenGLRenderer", "angle").toString(); + preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString); qputenv("QT_ANGLE_PLATFORM", "d3d11"); +#else + const QString preferredRendererString = kritarc.value("OpenGLRenderer", "auto").toString(); + preferredRenderer = KisOpenGL::convertConfigToOpenGLRenderer(preferredRendererString); #endif const QSurfaceFormat format = KisOpenGL::selectSurfaceFormat(preferredRenderer, rootSurfaceFormat, enableOpenGLDebug); if (format.renderableType() == QSurfaceFormat::OpenGLES) { QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true); } else { QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true); } KisOpenGL::setDefaultSurfaceFormat(format); KisOpenGL::setDebugSynchronous(openGLDebugSynchronous); #ifdef Q_OS_WIN // HACK: https://bugs.kde.org/show_bug.cgi?id=390651 resetRotation(); #endif } if (logUsage) { KisUsageLogger::initialize(); } QString root; QString language; { // Create a temporary application to get the root QCoreApplication app(argc, argv); Q_UNUSED(app); root = KoResourcePaths::getApplicationRoot(); QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); language = languageoverride.value(qAppName(), "").toString(); } #ifdef Q_OS_LINUX { QByteArray originalXdgDataDirs = qgetenv("XDG_DATA_DIRS"); if (originalXdgDataDirs.isEmpty()) { // We don't want to completely override the default originalXdgDataDirs = "/usr/local/share/:/usr/share/"; } qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share") + ":" + originalXdgDataDirs); } #else qputenv("XDG_DATA_DIRS", QFile::encodeName(root + "share")); #endif dbgKrita << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); // Now that the paths are set, set the language. First check the override from the language // selection dialog. dbgKrita << "Override language:" << language; bool rightToLeft = false; if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too qputenv("LANG", language.split(":").first().toLocal8Bit()); QLocale locale(language.split(":").first()); QLocale::setDefault(locale); const QStringList rtlLanguages = QStringList() << "ar" << "dv" << "he" << "ha" << "ku" << "fa" << "ps" << "ur" << "yi"; if (rtlLanguages.contains(language.split(':').first())) { rightToLeft = true; } } else { dbgKrita << "Qt UI languages:" << QLocale::system().uiLanguages() << qgetenv("LANG"); // And if there isn't one, check the one set by the system. QLocale locale = QLocale::system(); if (locale.name() != QStringLiteral("en")) { QStringList uiLanguages = locale.uiLanguages(); for (QString &uiLanguage : uiLanguages) { // This list of language codes that can have a specifier should // be extended whenever we have translations that need it; right // now, only en, pt, zh are in this situation. if (uiLanguage.startsWith("en") || uiLanguage.startsWith("pt")) { uiLanguage.replace(QChar('-'), QChar('_')); } else if (uiLanguage.startsWith("zh-Hant") || uiLanguage.startsWith("zh-TW")) { uiLanguage = "zh_TW"; } else if (uiLanguage.startsWith("zh-Hans") || uiLanguage.startsWith("zh-CN")) { uiLanguage = "zh_CN"; } } for (int i = 0; i < uiLanguages.size(); i++) { QString uiLanguage = uiLanguages[i]; // Strip the country code int idx = uiLanguage.indexOf(QChar('-')); if (idx != -1) { uiLanguage = uiLanguage.left(idx); uiLanguages.replace(i, uiLanguage); } } dbgKrita << "Converted ui languages:" << uiLanguages; qputenv("LANG", uiLanguages.first().toLocal8Bit()); #ifdef Q_OS_MAC // See https://bugs.kde.org/show_bug.cgi?id=396370 KLocalizedString::setLanguages(QStringList() << uiLanguages.first()); #else KLocalizedString::setLanguages(QStringList() << uiLanguages); #endif } } #if defined Q_OS_WIN && defined USE_QT_TABLET_WINDOWS && defined QT_HAS_WINTAB_SWITCH const bool forceWinTab = !KisConfig::useWin8PointerInputNoApp(&kritarc); QCoreApplication::setAttribute(Qt::AA_MSWindowsUseWinTabAPI, forceWinTab); if (qEnvironmentVariableIsEmpty("QT_WINTAB_DESKTOP_RECT") && qEnvironmentVariableIsEmpty("QT_IGNORE_WINTAB_MAPPING")) { QRect customTabletRect; KisDlgCustomTabletResolution::Mode tabletMode = KisDlgCustomTabletResolution::getTabletMode(&customTabletRect); KisDlgCustomTabletResolution::applyConfiguration(tabletMode, customTabletRect); } #endif // first create the application so we can create a pixmap KisApplication app(key, argc, argv); #ifdef HAVE_SET_HAS_BORDER_IN_FULL_SCREEN_DEFAULT if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL)) { QWindowsWindowFunctions::setHasBorderInFullScreenDefault(true); } #endif KisUsageLogger::writeHeader(); if (!language.isEmpty()) { if (rightToLeft) { app.setLayoutDirection(Qt::RightToLeft); } else { app.setLayoutDirection(Qt::LeftToRight); } } KLocalizedString::setApplicationDomain("krita"); dbgKrita << "Available translations" << KLocalizedString::availableApplicationTranslations(); dbgKrita << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); #ifdef Q_OS_WIN QDir appdir(KoResourcePaths::getApplicationRoot()); QString path = qgetenv("PATH"); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath() + ";" + path)); dbgKrita << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_DRMINGW tryInitDrMingw(); #endif // If we should clear the config, it has to be done as soon as possible after // KisApplication has been created. Otherwise the config file may have been read // and stored in a KConfig object we have no control over. app.askClearConfig(); KisApplicationArguments args(app); if (singleApplication && app.isRunning()) { // only pass arguments to main instance if they are not for batch processing // any batch processing would be done in this separate instance const bool batchRun = args.exportAs() || args.exportSequence(); if (!batchRun) { QByteArray ba = args.serialize(); if (app.sendMessage(ba)) { return 0; } } } if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } app.installEventFilter(KisQtWidgetsTweaker::instance()); if (!args.noSplash()) { // then create the pixmap from an xpm: we cannot get the // location of our datadir before we've started our components, // so use an xpm. QDate currentDate = QDate::currentDate(); QWidget *splash = 0; if (currentDate > QDate(currentDate.year(), 12, 4) || currentDate < QDate(currentDate.year(), 1, 9)) { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm), QPixmap(splash_holidays_x2_xpm)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm), QPixmap(splash_screen_x2_xpm)); } app.setSplashScreen(splash); } #if defined Q_OS_WIN KisConfig cfg(false); bool supportedWindowsVersion = true; #if QT_VERSION >= 0x050900 QOperatingSystemVersion osVersion = QOperatingSystemVersion::current(); if (osVersion.type() == QOperatingSystemVersion::Windows) { if (osVersion.majorVersion() >= QOperatingSystemVersion::Windows7.majorVersion()) { supportedWindowsVersion = true; } else { supportedWindowsVersion = false; if (cfg.readEntry("WarnedAboutUnsupportedWindows", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running an unsupported version of Windows: %1.\n" "This is not recommended. Do not report any bugs.\n" "Please update to a supported version of Windows: Windows 7, 8, 8.1 or 10.", osVersion.name())); cfg.writeEntry("WarnedAboutUnsupportedWindows", true); } } } #endif #ifndef USE_QT_TABLET_WINDOWS { if (cfg.useWin8PointerInput() && !KisTabletSupportWin8::isAvailable()) { cfg.setUseWin8PointerInput(false); } if (!cfg.useWin8PointerInput()) { bool hasWinTab = KisTabletSupportWin::init(); if (!hasWinTab && supportedWindowsVersion) { if (KisTabletSupportWin8::isPenDeviceAvailable()) { // Use WinInk automatically cfg.setUseWin8PointerInput(true); } else if (!cfg.readEntry("WarnedAboutMissingWinTab", false)) { if (KisTabletSupportWin8::isAvailable()) { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver and no Windows Ink pen devices are found. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } else { QMessageBox::information(nullptr, i18n("Krita Tablet Support"), i18n("Cannot load WinTab driver. If you have a drawing tablet, please make sure the tablet driver is properly installed."), QMessageBox::Ok, QMessageBox::Ok); } cfg.writeEntry("WarnedAboutMissingWinTab", true); } } } if (cfg.useWin8PointerInput()) { KisTabletSupportWin8 *penFilter = new KisTabletSupportWin8(); if (penFilter->init()) { // penFilter.registerPointerDeviceNotifications(); app.installNativeEventFilter(penFilter); dbgKrita << "Using Win8 Pointer Input for tablet support"; } else { dbgKrita << "No Win8 Pointer Input available"; delete penFilter; } } } #elif defined QT_HAS_WINTAB_SWITCH // Check if WinTab/WinInk has actually activated const bool useWinTabAPI = app.testAttribute(Qt::AA_MSWindowsUseWinTabAPI); if (useWinTabAPI != !cfg.useWin8PointerInput()) { cfg.setUseWin8PointerInput(useWinTabAPI); } #endif #endif if (!app.start(args)) { return 1; } #if QT_VERSION >= 0x050700 app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false); #endif // Set up remote arguments. QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)), &app, SLOT(remoteArguments(QByteArray,QObject*))); QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), &app, SLOT(fileOpenRequested(QString))); // Hardware information KisUsageLogger::write("\nHardware Information\n"); KisUsageLogger::write(QString(" GPU Acceleration: %1").arg(kritarc.value("OpenGLRenderer", "auto").toString())); KisUsageLogger::write(QString(" Memory: %1 Mb").arg(KisImageConfig(true).totalRAM())); KisUsageLogger::write(QString(" Number of Cores: %1").arg(QThread::idealThreadCount())); KisUsageLogger::write(QString(" Swap Location: %1\n").arg(KisImageConfig(true).swapDir())); int state = app.exec(); { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", "OPENGL_SUCCESS"); } if (logUsage) { KisUsageLogger::close(); } return state; } diff --git a/krita/org.kde.krita.appdata.xml b/krita/org.kde.krita.appdata.xml index ee3d525859..a07a6a1f1a 100644 --- a/krita/org.kde.krita.appdata.xml +++ b/krita/org.kde.krita.appdata.xml @@ -1,323 +1,338 @@ org.kde.krita org.kde.krita.desktop CC0-1.0 GPL-3.0-only Krita Foundation Fundació Krita Fundació Krita Krita Foundation Krita Foundation Krita Foundation Fundación Krita Krita Fundazioa Krita Foundation La Fondation Krita Fundación Krita Asas Krita Fondazione Krita Krita Foundation Krita Foundation Krita Foundation Fundacja Krity Fundação do Krita Krita Foundation Krita-stiftelsen Krita Vakfı Фундація Krita xxKrita Foundationxx Krita 基金会 Krita 基金會 foundation@krita.org Krita كريتا Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita Krita xxKritaxx Krita Krita Digital Painting, Creative Freedom رسم رقميّ، حريّة إبداعيّة Digitalno crtanje, kreativna sloboda Dibuix digital, Llibertat creativa Dibuix digital, Llibertat creativa Digitální malování, svoboda tvorby Digital tegning, kunstnerisk frihed Digitales Malen, kreative Freiheit Ψηφιακή ζωγραφική, δημιουργική ελευθερία Digital Painting, Creative Freedom Pintura digital, libertad creativa Digitaalne joonistamine, loominguline vabadus Margolan digitala, sormen askatasuna Digitaalimaalaus, luova vapaus Peinture numérique, liberté créatrice Debuxo dixital, liberdade creativa Pictura digital, Libertate creative Pelukisan Digital, Kebebasan Berkreatif Pittura digitale, libertà creativa 디지털 페인팅, 자유로운 창의성 Digital Painting, Creative Freedom Digital teikning – kreativ fridom Cyfrowe malowanie, Wolność Twórcza Pintura Digital, Liberdade Criativa Pintura digital, liberdade criativa Цифровое рисование. Творческая свобода Digitálne maľovanie, kreatívna sloboda Digital målning, kreativ frihet Sayısal Boyama, Yaratıcı Özgürlük Цифрове малювання, творча свобода xxDigital Painting, Creative Freedomxx 自由挥洒数字绘画的无限创意 數位繪畫,創作自由

Krita is the full-featured digital art studio.

Krita je potpuni digitalni umjetnički studio.

El Krita és l'estudi d'art digital ple de funcionalitats.

-

Krita és l'estudi d'art digital ple de funcionalitats.

+

El Krita és l'estudi d'art digital ple de funcionalitats.

Krita ist ein digitales Designstudio mit umfangreichen Funktionen.

Το Krita είναι ένα πλήρες χαρακτηριστικών ψηφιακό ατελιέ.

Krita is the full-featured digital art studio.

Krita es un estudio de arte digital completo

Krita on rohkete võimalustega digitaalkunstistuudio.

Krita arte lantegi digital osoa da.

Krita on täyspiirteinen digitaiteen ateljee.

Krita est le studio d'art numérique complet.

Krita é un estudio completo de arte dixital.

Krita es le studio de arte digital complete.

Krita adalah studio seni digital yang penuh dengan fitur.

Krita è uno studio d'arte digitale completo.

Krita は、フル機能を備えたデジタルなアートスタジオです。

Krita는 디지털 예술 스튜디오입니다.

Krita is de digitale kunststudio vol mogelijkheden.

Krita er ei funksjonsrik digital teiknestove.

Krita jest pełnowymiarowym, cyfrowym studiem artystycznym

O Krita é o estúdio de arte digital completo.

O Krita é o estúdio de arte digital completo.

Krita — полнофункциональный инструмент для создания цифровой графики.

Krita je plne vybavené digitálne umelecké štúdio.

Krita är den fullfjädrade digitala konststudion.

Krita, tam özellikli dijital sanat stüdyosudur.

Krita — повноцінний комплекс для створення цифрових художніх творів.

xxKrita is the full-featured digital art studio.xx

Krita 是一款功能齐全的数字绘画工作室软件。

Krita 是全功能的數位藝術工作室。

It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.

On je savršen za skiciranje i slikanje i predstavlja finalno rješenje za kreiranje digitalnih slika od nule s majstorima

És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.

És perfecte per fer esbossos i pintar, i presenta una solució final per crear fitxers de dibuix digital des de zero per a mestres.

Είναι ιδανικό για σκιτσογραφία και ζωγραφική, και παρουσιάζει μια από άκρη σε άκρη λύση για τη δημιουργία από το μηδέν αρχείων ψηφιακης ζωγραφικής από τους δασκάλους της τέχνης.

It is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.

Es perfecto para diseñar y pintar, y ofrece una solución completa para crear desde cero archivos de pintura digital apta para profesionales.

See on suurepärane töövahend visandite ja joonistuste valmistamiseks ning annab andekatele kunstnikele võimaluse luua digitaalpilt algusest lõpuni just oma käe järgi.

Zirriborratzeko eta margotzeko ezin hobea da, eta margolan digitalen fitxategiak hutsetik sortzeko muturretik-muturrera konponbide bat aurkezten du, maisuentzako mailakoa.

Se on täydellinen luonnosteluun ja maalaukseen ja tarjoaa kokonaisratkaisun digitaalisten kuvatiedostojen luomiseen alusta alkaen.

Il est parfait pour crayonner et peindre, et constitue une solution de bout en bout pour créer des fichier de peinture numérique depuis la feuille blanche jusqu'au épreuves finales.

Resulta perfecto para debuxar e pintar, e presenta unha solución completa que permite aos mestres crear ficheiros de debuxo dixital desde cero.

Illo es perfecte pro schizzar e pinger, e presenta un solution ab fin al fin pro crear files de pictura digital ab grattamentos per maestros.

Ini adalah sempurna untuk mensketsa dan melukis, dan menghadirkan sebuah solusi untuk menciptakan file-file pelukisan digital dari goresan si pelukis ulung.

Perfetto per fare schizzi e dipingere, prevede una soluzione completa che consente agli artisti di creare file di dipinti digitali partendo da zero.

스케치, 페인팅을 위한 완벽한 도구이며, 생각에서부터 디지털 페인팅 파일을 만들어 낼 수 있는 종합적인 도구를 제공합니다.

Het is perfect voor schetsen en schilderen en zet een end–to–end oplossing voor het maken van digitale bestanden voor schilderingen vanuit het niets door meesters.

Passar perfekt for både teikning og måling, og dekkjer alle ledd i prosessen med å laga digitale målerifiler frå grunnen av.

Nadaje się perfekcyjnie do szkicowania i malowania i dostarcza zupełnego rozwiązania dla tworzenia plików malowideł cyfrowych od zalążka.

É perfeito para desenhos e pinturas, oferecendo uma solução final para criar ficheiros de pintura digital do zero por mestres.

É perfeito para desenhos e pinturas, oferecendo uma solução final para criar arquivos de desenho digital feitos a partir do zero por mestres.

Она превосходно подходит для набросков и рисования, предоставляя мастерам самодостаточный инструмент для создания цифровой живописи с нуля.

Je ideálna na skicovanie a maľovanie a poskytuje end-to-end riešenie na vytváranie súborov digitálneho maľovania od základu od profesionálov.

Den är perfekt för att skissa och måla, samt erbjuder en helomfattande lösning för att skapa digitala målningsfiler från grunden av mästare.

Eskiz ve boyama için mükemmeldir ve ustaların sıfırdan dijital boyama dosyaları oluşturmak için uçtan-uca bir çözüm sunar.

Цей комплекс чудово пасує для створення ескізів та художніх зображень і є самодостатнім набором для створення файлів цифрових полотен «з нуля» для справжніх художників.

xxIt is perfect for sketching and painting, and presents an end–to–end solution for creating digital painting files from scratch by masters.xx

它专门为数字绘画设计,为美术工作者提供了一个从起草、上色到完成作品等整个创作流程的完整解决方案。

它是素描和繪畫的完美選擇,並提供了一個從零開始建立數位繪畫檔的端到端解決方案。

Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.

Krita je odličan izbor za kreiranje konceptualne umjetnosti, stripove, teksture za obradu i mat slike. Krita podržava mnoge prostore boja kao RGB i CMIK na 8 i 16 bitnim cjelobrojnim kanalimaa, kao i 16 i 32 bita floating point kanalima.

El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.

El Krita és una gran elecció per crear art conceptual, còmics, textures per renderitzar i pintures «matte». El Krita permet molts espais de color com el RGB i el CMYK a 8 i 16 bits de canals sencers, així com 16 i 32 bits de canals de coma flotant.

Το Krita είναι μια εξαιρετική επιλογή για τη δημιουργία αφηρημένης τέχνης, ιστοριών με εικόνες, υφής για ζωγραφική αποτύπωσης και διάχυσης φωτός. Το Krita υποστηρίζει πολλούς χρωματικούς χώρους όπως τα RGB και CMYK σε 8 και 16 bit κανάλια ακεραίων καθώς επίσης και σε 16 και 32 bit κανάλια κινητής υποδιαστολής,

Krita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colourspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.

Krita es una gran elección para crear arte conceptual, cómics, texturas para renderizar y «matte paintings». Krita permite el uso de muchos espacios de color, como, por ejemplo, RGB y CMYK, tanto en canales de enteros de 8 y 16 bits, así como en canales de coma flotante de 16 y 32 bits.

Krita on üks paremaid valikuid kontseptuaalkunsti, koomiksite, tekstuuride ja digitaalmaalide loomiseks. Krita toetab paljusid värviruume, näiteks RGB ja CMYK 8 ja 16 täisarvulise bitiga kanali kohta, samuti 16 ja 32 ujukomabitiga kanali kohta.

Krita aukera bikaina da kontzeptuzko artea, komikiak, errendatzeko ehundurak eta «matte» margolanak sortzeko. Kritak kolore-espazio ugari onartzen ditu hala nola GBU eta CMYK, 8 eta 16 biteko osoko kanaletan, baita 16 eta 32 biteko koma-higikorreko kanaletan.

Krita on hyvä valinta konseptikuvituksen, sarjakuvien, pintakuvioiden ja maalausten luomiseen. Krita tukee useita väriavaruuksia kuten RGB:tä ja CMYK:ta 8 ja 16 bitin kokonaisluku- samoin kuin 16 ja 32 bitin liukulukukanavin.

Krita est un très bon choix pour créer des concepts arts, des bandes-dessinées, des textures de rendu et des peintures. Krita prend en charge plusieurs espaces de couleurs comme RVB et CMJN avec les canaux de 8 et 16 bits entiers ainsi que les canaux de 16 et 32 bits flottants.

Krita é unha gran opción para crear arte conceptual, texturas para renderización e pinturas mate. Krita permite usar moitos espazos de cores como RGB e CMYK con canles de 8 e 16 bits, así como canles de coma flotante de 16 e 32 bits.

Krita es un grande selection pro crear arte de concepto, comics, texturas pro rendering e picturas opac. Krita supporta multe spatios de colores como RGB e CMYK con canales de integer a 8 e 16 bits, como anque canales floating point a 16 e 32 bits.

Krita adalah pilihan yang cocok untuk menciptakan konsep seni, komik, tekstur untuk rendering dan lukisan matte. Krita mendukung banyak ruang warna seperti RGB dan CMYK pada channel integer 8 dan 16 bit, serta channel floating point 16 dan 32 bit.

Krita rappresenta una scelta ottimale per la creazione di arte concettuale, fumetti e texture per il rendering e il matte painting. Krita supporta molti spazi colori come RGB e CMYK a 8 e 16 bit per canali interi e 16 e 32 bit per canali a virgola mobile.

コンセプトアート、コミック、3DCG 用テクスチャ、マットペイントを制作する方にとって、Krita は最適な選択です。Krita は、8/16 ビット整数/チャンネル、および 16/32 ビット浮動小数点/チャンネルの RGB や CMYK をはじめ、さまざまな色空間をサポートしています。

Krita는 컨셉 아트, 만화, 렌더링용 텍스처, 풍경화 등을 그릴 때 사용할 수 있는 완벽한 도구입니다. RGB, CMYK와 같은 여러 색 공간 및 8비트/16비트 정수 채널, 16비트/32비트 부동 소수점 채널을 지원합니다.

Krita is een goede keuze voor het maken van kunstconcepten, strips, textuur voor weergeven en matte schilderijen. Krita ondersteunt vele kleurruimten zoals RGB en CMYK in 8 en 16 bits kanalen met gehele getallen, evenals 16 en 32 bits kanalen met drijvende komma.

Krita er det ideelle valet dersom du vil laga konseptskisser, teikneseriar, teksturar for 3D-rendering eller «matte paintings». Programmet støttar fleire fargerom, som RGB og CMYK med 8- og 16-bits heiltals- eller flyttalskanalar.

Krita jest świetnym wyborem przy tworzeniu koncepcyjnej sztuki, komiksów, tekstur do wyświetlania i kaszet. Krita obsługuje wiele przestrzeni barw takich jak RGB oraz CMYK dla kanałów 8 oraz 16 bitowych wyrażonych w l. całkowitych, a także 16 oraz 32 bitowych wyrażonych w l. zmiennoprzecinkowych.

O Krita é uma óptima escolha para criar arte conceptual, banda desenhada, texturas para desenho e pinturas. O Krita suporta diversos espaços de cores como o RGB e o CMYK com canais de cores inteiros a 8 e 16 bits, assim como canais de vírgula flutuante a 16 e a 32 bits.

O Krita é uma ótima escolha para criação de arte conceitual, histórias em quadrinhos, texturas para desenhos e pinturas. O Krita tem suporte a diversos espaços de cores como RGB e CMYK com canais de cores inteiros de 8 e 16 bits, assim como canais de ponto flutuante de 16 e 32 bits.

Krita — отличный выбор для создания концепт-артов, комиксов, текстур для рендеринга и рисования. Она поддерживает множество цветовых пространств включая RGB и CMYK с 8 и 16 целыми битами на канал, а также 16 и 32 битами с плавающей запятой на канал.

Krita je výborná voľba pre vytváranie konceptového umenia, textúr na renderovanie a matné kresby. Krita podporuje mnoho farebných priestorov ako RGB a CMYK na 8 a 16 bitových celočíselných kanáloch ako aj 16 a 32 bitových reálnych kanáloch.

Krita är ett utmärkt val för att skapa concept art, serier, strukturer för återgivning och bakgrundsmålningar. Krita stöder många färgrymder som RGB och CMYK med 8- och 16-bitars heltal, samt 16- och 32-bitars flyttal.

Krita, konsept sanat, çizgi roman, kaplama ve mat resimler için dokular oluşturmak için mükemmel bir seçimdir. Krita, 8 ve 16 bit tamsayı kanallarında RGB ve CMYK gibi birçok renk alanını ve 16 ve 32 bit kayan nokta kanallarını desteklemektedir.

Krita — чудовий інструмент для створення концептуального живопису, коміксів, текстур для моделей та декорацій. У Krita передбачено підтримку багатьох просторів кольорів, зокрема RGB та CMYK з 8-бітовими та 16-бітовими цілими значеннями, а також 16-бітовими та 32-бітовими значеннями з рухомою крапкою для каналів кольорів.

xxKrita is a great choice for creating concept art, comics, textures for rendering and matte paintings. Krita supports many colorspaces like RGB and CMYK at 8 and 16 bits integer channels, as well as 16 and 32 bits floating point channels.xx

Krita 是绘制概念美术、漫画、纹理和电影布景的理想选择。Krita 支持多种色彩空间,如 8 位和 16 位整数及 16 位和 32 位浮点的 RGB 和 CMYK 颜色模型。

Krita 是創造概念藝術、漫畫、彩現紋理和場景繪畫的絕佳選擇。Krita 在 8 位元和 16 位元整數色版,以及 16 位元和 32 位元浮點色板中支援 RGB 和 CMYK 等多種色彩空間。

Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.

Zabavite se kreirajući napredne pogone četki, filtere i mnoge praktične osobine koje čine Krita vrlo produktivnim.

Gaudiu pintant amb els motors avançats de pinzells, filtres impressionants i moltes característiques útils que fan el Krita molt productiu.

Gaudiu pintant amb els motors avançats de pinzells, filtres impressionants i moltes característiques útils que fan el Krita molt productiu.

Διασκεδάστε ζωγραφίζοντας με τις προηγμένες μηχανές πινέλων, με εκπληκτικά φίλτρα και πολλά εύκολης χρήσης χαρακτηριστικά που παρέχουν στο Krita εξαιρετικά αυξημένη παραγωγικότητα.

Have fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.

Diviértase pintando con los avanzados motores de pinceles, los espectaculares filtros y muchas funcionalidades prácticas que hacen que Krita sea enormemente productivo.

Joonistamise muudavad tunduvalt lõbusamaks võimsad pintslimootorid, imetabased filtrid ja veel paljud käepärased võimalused, mis muudavad Krita kasutaja tohutult tootlikuks.

Marrazten ondo pasa ezazu, isipu motor aurreratuekin, iragazki txundigarriekin eta eginbide praktiko ugariekin, zeintzuek Krita ikaragarri emankorra egiten duten.

Pidä hauskaa maalatessasi edistyneillä sivellinmoottoreilla, hämmästyttävillä suotimilla ja monilla muilla kätevillä ominaisuuksilla, jotka tekevät Kritasta tavattoman tehokkaan.

Amusez-vous à peindre avec les outils de brosse avancés, les filtres incroyables et les nombreuses fonctionnalités pratiques qui rendent Krita extrêmement productif.

Goza debuxando con motores de pincel avanzados, filtros fantásticos e moitas outras funcionalidades útiles que fan de Krita un programa extremadamente produtivo.

Amusa te a pinger con le motores de pincel avantiate, filtros stupende e multe characteristicas amical que face Krita enormemente productive.

Bersenang-senanglah melukis dengan mesin kuas canggih, filter luar biasa dan banyak fitur berguna yang membuat Krita sangat produktif.

Divertiti a dipingere con gli avanzati sistemi di pennelli, i sorprendenti filtri e molte altre utili caratteristiche che fanno di Krita un software enormemente produttivo.

Krita のソフトウェアとしての生産性を高めている先進的なブラシエンジンや素晴らしいフィルタのほか、便利な機能の数々をお楽しみください。

Krita의 고급 브러시 엔진, 다양한 필터, 여러 도움이 되는 기능으로 생산성을 즐겁게 향상시킬 수 있습니다.

Veel plezier met schilderen met the geavanceerde penseel-engines, filters vol verbazing en vele handige mogelijkheden die maken dat Krita enorm productief is.

Leik deg med avanserte penselmotorar og fantastiske biletfilter – og mange andre nyttige funksjonar som gjer deg produktiv med Krita.

Baw się przy malowaniu przy użyciu zaawansowanych silników pędzli, zadziwiających filtrów i wielu innych przydatnych cech, które czynią z Krity bardzo produktywną.

Divirta-se a pintar com os motores de pincéis avançados, os filtros espantosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.

Divirta-se pintando com os mecanismos de pincéis avançados, filtros maravilhosos e muitas outras funcionalidades úteis que tornam o Krita altamente produtivo.

Получайте удовольствие от использования особых кистевых движков, впечатляющих фильтров и множества других функций, делающих Krita сверхпродуктивной.

Užívajte si maľovanie s pokročilými kresliacimi enginmi, úžasnými filtrami a mnohými užitočnými funkciami, ktoré robia Kritu veľmi produktívnu.

Ha det så kul vid målning med de avancerade penselfunktionerna, fantastiska filtren och många praktiska funktioner som gör Krita så enormt produktiv.

Gelişmiş fırça motorları, şaşırtıcı filtreler ve Krita'yı son derece üretken yapan bir çok kullanışlı özellikli boya ile iyi eğlenceler.

Отримуйте задоволення від малювання за допомогою пензлів з найширшими можливостями, чудових фільтрів та багатьох зручних можливостей, які роблять Krita надзвичайно продуктивним засобом малювання.

xxHave fun painting with the advanced brush engines, amazing filters and many handy features that make Krita enormously productive.xx

Krita 具有功能强大的笔刷引擎、种类繁多的滤镜以及便于操作的交互设计,可让你尽情、高效地挥洒无限创意。

使用先進的筆刷引擎、驚人的濾鏡和許多方便的功能來開心地繪畫,讓 Krita 擁有巨大的生產力。

https://www.krita.org/ https://docs.krita.org/KritaFAQ.html https://krita.org/support-us/donations/ https://docs.krita.org/ https://docs.krita.org/en/untranslatable_pages/reporting_bugs.html Krita is a full-featured digital painting studio. Krita és un estudi de pintura digital ple de funcionalitats. Krita és un estudi de pintura digital ple de funcionalitats. Krita es un completo estudio de dibujo digital. + Krita adalah studio pelukisan digital dengan fitur yang lengkap. Krita è uno studio d'arte digitale completo. Krita는 다기능 디지털 예술 스튜디오입니다. Krita is een digitale schilderstudio vol mogelijkheden. + Krita jest pełnowymiarowym, cyfrowym studiem artystycznym. O Krita é um estúdio de arte digital completo. Krita är en fullfjädrad digital konststudio. Krita — повноцінний комплекс для цифрового малювання. xxKrita is a full-featured digital painting studio.xx + Krita 是全功能的數位繪圖工作室。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_001.png The startup window now also gives you the latest news about Krita. La finestra d'inici ara proporciona les darreres noticies quant al Krita. La finestra d'inici ara proporciona les darreres noticies quant al Krita. La ventana de bienvenida también le proporciona ahora las últimas noticias sobre Krita. + Window pemulaian kini juga memberikan kamu kabar terbaru tentang Krita. La finestra di avvio ora fornisce anche le ultime novità su Krita. 시작 창에서 Krita의 최신 소식을 볼 수 있습니다. Het opstartvenster geeft u nu ook you het laatste nieuws over Krita. + Okno początkowe teraz wyświetla wieści o Kricie. A janela inicial agora também lhe dá as últimas notícias sobre o Krita. Startfönstret ger nu också senaste nytt om Krita. У початковому вікні програми ви можете бачити найсвіжіші новини щодо Krita. xxThe startup window now also gives you the latest news about Krita.xx + 開始視窗也提供給您關於 Krita 的最新消息。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_002.png There are over ten immensely powerful brush engines. Hi ha uns deu motors de pinzell immensament potents. Hi ha uns deu motors de pinzell immensament potents. Existen unos diez inmensamente potentes motores de pinceles. + Ada lebih dari sepuluh mesin kuas yang sangat manjur. Ci sono oltre dieci motori di pennelli incredibilmente potenti. 10가지 종류의 강력한 브러시 엔진을 사용할 수 있습니다. Er zijn meer dan tien immens krachtige penseelengines. + Istnieje ponad dziesięć zaawansowanych silników pędzli. Existem mais de dez motores de pincéis extremamente poderosos. Det finns mer än tio enormt kraftfulla penselgränssnitt. У програмі передбачено понад десяток надзвичайно потужних рушіїв пензлів. xxThere are over ten immensely powerful brush engines.xx https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_003.png Create and use gamut masks to give your images a coherent feel. Creeu i useu màscares de gamma per donar a les imatges una aparença coherent. Creeu i useu màscares de gamma per donar a les imatges una aparença coherent. Cree y use gamas para proporcionar a sus imágenes un aspecto coherente. + Ciptakan dan gunakan masker gamut untuk memberikan gambarmu sebuah suasana koheren. Crea e utilizza maschere gamut per dare alle tue immagini un aspetto coerente. 색역 마스크를 만들고 사용할 수 있습니다. Maak en gebruik gamut-maskers om uw afbeeldingen een coherent gevoel te geven. + Stwórz i używaj masek gamut, aby nadać swoim obrazom spójny wygląd. Crie e use máscaras de gamute para dar às suas imagens uma aparência coerente. Att skapa och använda färgomfångsmasker ger bilder en sammanhängande känsla. Створюйте маски палітри і користуйтеся ними для надання вашим малюнкам однорідного вигляду. xxCreate and use gamut masks to give your images a coherent feel.xx https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_004.png Into animation? Krita provides everything you need for traditional, hand-drawn animation. Esteu amb animacions? El Krita proporciona tol el que cal per a l'animació a mà tradicional. Esteu amb animacions? El Krita proporciona tol el que cal per a l'animació a mà tradicional. ¿Trabaja con animación? Krita proporciona todo lo necesario para la animación manual tradicional. + Soal animasi? krita menyediakan apa pun yang kamu perlukan untuk animasi gambar-tangan, tradisional. Per le animazioni? Krita fornisce tutto ciò che ti server per l'animazione tradizionale, disegnata a mano. 애니메이션을 만들 계획이 있으신가요? Krita를 통해서 수작업 애니메이션을 작업할 수 있습니다. Naar animatie? Krita biedt alles wat u nodig hebt voor traditionele, met de hand getekende animatie. + Zajmujesz się animacjami? Krita zapewnia wszystko czego potrzebujesz do tworzenia tradycyjnych, ręcznie rysowanych animacji. Gosta de animação? O Krita oferece tudo o que precisa para o desenho animado tradicional e desenhado à mão. Gillar du animering? Krita tillhandahåller allt som behövs för traditionella, handritade animeringar. Працюєте із анімацією? У Krita ви знайдете усе, що потрібно для створення традиційної, намальованої вручну анімації. xxInto animation? Krita provides everything you need for traditional, hand-drawn animation.xx + 想做動畫?Krita 提供您在傳統、手繪動畫所需的任何東西。 https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_005.png If you're new to digital painting, or want to know more about Krita's possibilities, there's an extensive, up-to-date manual. Si sou nou a la pintura digital, o voleu conèixer més quant a les possibilitats del Krita, hi ha un ampli manual actualitzat. Si sou nou a la pintura digital, o voleu conèixer més quant a les possibilitats del Krita, hi ha un ampli manual actualitzat. Si está empezando con el dibujo digital o si quiere saber más sobre la posibilidades de Krita, dispone de un extenso y actualizado manual. + Jika kamu baru dalam pelukisan digital, atau ingin mengetahui selebihnya tentang Krita, di situ ada manual yang update dan luas. Se sei nuovo del disegno digitale, o vuoi saperne di più sulle possibilità di Krita, è disponibile un manuale completo e aggiornato. 디지털 페인팅을 처음 시작하시거나, Krita의 기능을 더 알아 보려면 사용 설명서를 참조하십시오. Als u nieuw bent in digitaal schilderen of u wilt meer weten over de mogelijkheden van Krita, dan is er een uitgebreide, bijgewerkte handleiding. + Jeśli cyfrowe malowanie to dla ciebie nowość, lub jeśli chcesz dowiedzieć się więcej o możliwościach Krity, to dostępny jest wyczerpująca i aktualna instrukcja obsługi. Se é novo na pintura digital, ou deseja saber mais sobre as possibilidades do Krita, existe um manual extenso e actualizado. Om digital målning är nytt för dig, eller om du vill veta mer om Kritas möjligheter, finns en omfattande, aktuell handbok. Якщо ви не маєте достатнього досвіду у цифровому малюванні або хочете дізнатися більше про можливості Krita, скористайтеся нашим докладним і актуальним підручником. xxIf you're new to digital painting, or want to know more about Krita's possibilities, there's an extensive, up-to-date manual.xx https://cdn.kde.org/screenshots/krita/2018-03-17_screenshot_006.png Graphics KDE krita org.kde.krita.desktop
diff --git a/libs/flake/KisGamutMaskViewConverter.cpp b/libs/flake/KisGamutMaskViewConverter.cpp index 2135f61c2e..dd499dfe51 100644 --- a/libs/flake/KisGamutMaskViewConverter.cpp +++ b/libs/flake/KisGamutMaskViewConverter.cpp @@ -1,174 +1,175 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisGamutMaskViewConverter.h" #include #include #include #include //#define DEBUG_GAMUT_MASK_CONVERTER KisGamutMaskViewConverter::KisGamutMaskViewConverter() : m_viewSize(1.0) , m_maskSize(QSizeF(1,1)) , m_maskResolution(1) { computeAndSetZoom(); } KisGamutMaskViewConverter::~KisGamutMaskViewConverter() { } QSize KisGamutMaskViewConverter::viewSize() const { return QSize(m_viewSize, m_viewSize); } QPointF KisGamutMaskViewConverter::documentToView(const QPointF &documentPoint) const { return QPointF(documentToViewX(documentPoint.x()), documentToViewY(documentPoint.y())); } QPointF KisGamutMaskViewConverter::viewToDocument(const QPointF &viewPoint) const { return QPointF(viewToDocumentX(viewPoint.x()), viewToDocumentY(viewPoint.y())); } QRectF KisGamutMaskViewConverter::documentToView(const QRectF &documentRect) const { return QRectF(documentToView(documentRect.topLeft()), documentToView(documentRect.size())); } QRectF KisGamutMaskViewConverter::viewToDocument(const QRectF &viewRect) const { return QRectF(viewToDocument(viewRect.topLeft()), viewToDocument(viewRect.size())); } QSizeF KisGamutMaskViewConverter::documentToView(const QSizeF &documentSize) const { return QSizeF(documentToViewX(documentSize.width()), documentToViewY(documentSize.height())); } QSizeF KisGamutMaskViewConverter::viewToDocument(const QSizeF &viewSize) const { return QSizeF(viewToDocumentX(viewSize.width()), viewToDocumentY(viewSize.height())); } qreal KisGamutMaskViewConverter::documentToViewX(qreal documentX) const { qreal translated = documentX * m_zoomLevel; #ifdef DEBUG_GAMUT_MASK_CONVERTER debugFlake << "KisGamutMaskViewConverter::DocumentToViewX: " << "documentX: " << documentX << " -> translated: " << translated; #endif return translated; } qreal KisGamutMaskViewConverter::documentToViewY(qreal documentY) const { qreal translated = documentY * m_zoomLevel; #ifdef DEBUG_GAMUT_MASK_CONVERTER debugFlake << "KisGamutMaskViewConverter::DocumentToViewY: " << "documentY: " << documentY << " -> translated: " << translated; #endif return translated; } qreal KisGamutMaskViewConverter::viewToDocumentX(qreal viewX) const { qreal translated = viewX / m_zoomLevel; #ifdef DEBUG_GAMUT_MASK_CONVERTER debugFlake << "KisGamutMaskViewConverter::viewToDocumentX: " << "viewX: " << viewX << " -> translated: " << translated; #endif return translated; } qreal KisGamutMaskViewConverter::viewToDocumentY(qreal viewY) const { qreal translated = viewY / m_zoomLevel; #ifdef DEBUG_GAMUT_MASK_CONVERTER debugFlake << "KisGamutMaskViewConverter::viewToDocumentY: " << "viewY: " << viewY << " -> translated: " << translated; #endif return translated; } void KisGamutMaskViewConverter::setZoom(qreal zoom) { if (qFuzzyCompare(zoom, qreal(0.0)) || qFuzzyCompare(zoom, qreal(1.0))) { zoom = 1; } #ifdef DEBUG_GAMUT_MASK_CONVERTER debugFlake << "KisGamutMaskViewConverter::setZoom: setting to " << zoom; #endif m_zoomLevel = zoom; } void KisGamutMaskViewConverter::zoom(qreal *zoomX, qreal *zoomY) const { *zoomX = m_zoomLevel; *zoomY = m_zoomLevel; } void KisGamutMaskViewConverter::setViewSize(QSize viewSize) { m_viewSize = viewSize.width(); computeAndSetZoom(); } void KisGamutMaskViewConverter::setMaskSize(QSizeF maskSize) { m_maskSize = maskSize; m_maskResolution = maskSize.width(); computeAndSetZoom(); } void KisGamutMaskViewConverter::computeAndSetZoom() { qreal zoom = m_viewSize / m_maskResolution; #ifdef DEBUG_GAMUT_MASK_CONVERTER debugFlake << "KisGamutMaskViewConverter::computeAndSetZoom: " << "m_viewSize: " << m_viewSize << " m_maskSize: " << m_maskResolution; #endif setZoom(zoom); } diff --git a/libs/flake/KisGamutMaskViewConverter.h b/libs/flake/KisGamutMaskViewConverter.h index 73073efae8..6155528abf 100644 --- a/libs/flake/KisGamutMaskViewConverter.h +++ b/libs/flake/KisGamutMaskViewConverter.h @@ -1,70 +1,71 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISGAMUTMASKVIEWCONVERTER_H #define KISGAMUTMASKVIEWCONVERTER_H #include "kritaflake_export.h" #include #include #include class QPointF; class QRectF; /** * @brief view convertor for gamut mask calculations and painting */ class KRITAFLAKE_EXPORT KisGamutMaskViewConverter : public KoViewConverter { public: KisGamutMaskViewConverter(); ~KisGamutMaskViewConverter(); QSize viewSize() const; void setViewSize(QSize viewSize); void setMaskSize(QSizeF maskSize); QPointF documentToView(const QPointF &documentPoint) const override; QPointF viewToDocument(const QPointF &viewPoint) const override; QRectF documentToView(const QRectF &documentRect) const override; QRectF viewToDocument(const QRectF &viewRect) const override; QSizeF documentToView(const QSizeF& documentSize) const override; QSizeF viewToDocument(const QSizeF& viewSize) const override; qreal documentToViewX(qreal documentX) const override; qreal documentToViewY(qreal documentY) const override; qreal viewToDocumentX(qreal viewX) const override; qreal viewToDocumentY(qreal viewY) const override; void setZoom(qreal zoom) override; void zoom(qreal *zoomX, qreal *zoomY) const override; private: void computeAndSetZoom(); qreal m_zoomLevel; // 1.0 is 100% int m_viewSize; QSizeF m_maskSize; qreal m_maskResolution; }; #endif // KISGAMUTMASKVIEWCONVERTER_H diff --git a/libs/flake/resources/KoGamutMask.cpp b/libs/flake/resources/KoGamutMask.cpp index 5fecaea1bb..d7da355b0d 100644 --- a/libs/flake/resources/KoGamutMask.cpp +++ b/libs/flake/resources/KoGamutMask.cpp @@ -1,427 +1,428 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoGamutMask.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KoGamutMaskShape::KoGamutMaskShape(KoShape* shape) : m_maskShape(shape) , m_shapePaintingContext(KoShapePaintingContext()) { } KoGamutMaskShape::KoGamutMaskShape() {}; KoGamutMaskShape::~KoGamutMaskShape() {}; KoShape* KoGamutMaskShape::koShape() { return m_maskShape; } bool KoGamutMaskShape::coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter, int maskRotation) const { // apply mask rotation to coord const KisGamutMaskViewConverter& converter = dynamic_cast(viewConverter); QPointF centerPoint(converter.viewSize().width()*0.5, converter.viewSize().height()*0.5); QTransform rotationTransform; rotationTransform.translate(centerPoint.x(), centerPoint.y()); rotationTransform.rotate(-maskRotation); rotationTransform.translate(-centerPoint.x(), -centerPoint.y()); QPointF rotatedCoord = rotationTransform.map(coord); QPointF translatedPoint = viewConverter.viewToDocument(rotatedCoord); bool isClear = m_maskShape->hitTest(translatedPoint); return isClear; } void KoGamutMaskShape::paint(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation) { painter.save(); // apply mask rotation before drawing QPointF centerPoint(painter.viewport().width()*0.5, painter.viewport().height()*0.5); painter.translate(centerPoint); painter.rotate(maskRotation); painter.translate(-centerPoint); painter.setTransform(m_maskShape->absoluteTransformation(&viewConverter) * painter.transform()); m_maskShape->paint(painter, viewConverter, m_shapePaintingContext); painter.restore(); } void KoGamutMaskShape::paintStroke(QPainter &painter, const KoViewConverter &viewConverter, int maskRotation) { painter.save(); // apply mask rotation before drawing QPointF centerPoint(painter.viewport().width()*0.5, painter.viewport().height()*0.5); painter.translate(centerPoint); painter.rotate(maskRotation); painter.translate(-centerPoint); painter.setTransform(m_maskShape->absoluteTransformation(&viewConverter) * painter.transform()); m_maskShape->paintStroke(painter, viewConverter, m_shapePaintingContext); painter.restore(); } struct Q_DECL_HIDDEN KoGamutMask::Private { QString name; QString title; QString description; QByteArray data; QVector maskShapes; QVector previewShapes; QSizeF maskSize; int rotation; }; KoGamutMask::KoGamutMask(const QString& filename) : KoResource(filename) , d(new Private()) { d->maskSize = QSizeF(144.0,144.0); setRotation(0); } KoGamutMask::KoGamutMask() : KoResource(QString()) , d(new Private()) { d->maskSize = QSizeF(144.0,144.0); setRotation(0); } KoGamutMask::KoGamutMask(KoGamutMask* rhs) : QObject(0) , KoResource(QString()) , d(new Private()) { setFilename(rhs->filename()); setTitle(rhs->title()); setDescription(rhs->description()); d->maskSize = rhs->d->maskSize; QList newShapes; for(KoShape* sh: rhs->koShapes()) { newShapes.append(sh->cloneShape()); } setMaskShapes(newShapes); setValid(true); } bool KoGamutMask::coordIsClear(const QPointF& coord, KoViewConverter &viewConverter, bool preview) { QVector* shapeVector; if (preview && !d->previewShapes.isEmpty()) { shapeVector = &d->previewShapes; } else { shapeVector = &d->maskShapes; } for(KoGamutMaskShape* shape: *shapeVector) { if (shape->coordIsClear(coord, viewConverter, rotation()) == true) { return true; } } return false; } void KoGamutMask::paint(QPainter &painter, KoViewConverter& viewConverter, bool preview) { QVector* shapeVector; if (preview && !d->previewShapes.isEmpty()) { shapeVector = &d->previewShapes; } else { shapeVector = &d->maskShapes; } for(KoGamutMaskShape* shape: *shapeVector) { shape->paint(painter, viewConverter, rotation()); } } void KoGamutMask::paintStroke(QPainter &painter, KoViewConverter &viewConverter, bool preview) { QVector* shapeVector; if (preview && !d->previewShapes.isEmpty()) { shapeVector = &d->previewShapes; } else { shapeVector = &d->maskShapes; } for(KoGamutMaskShape* shape: *shapeVector) { shape->paintStroke(painter, viewConverter, rotation()); } } bool KoGamutMask::load() { QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnFlake << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool KoGamutMask::loadFromDevice(QIODevice *dev) { if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); // TODO: test KIS_ASSERT_RECOVER_RETURN_VALUE(d->data.size() != 0, false); if (filename().isNull()) { warnFlake << "Cannot load gamut mask" << name() << "there is no filename set"; return false; } if (d->data.isNull()) { QFile file(filename()); if (file.size() == 0) { warnFlake << "Cannot load gamut mask" << name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); d->data = file.readAll(); file.close(); } QBuffer buf(&d->data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-gamutmask", KoStore::Zip)); if (!store || store->bad()) return false; bool storeOpened = store->open("gamutmask.svg"); if (!storeOpened) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QString errorMsg; int errorLine = 0; int errorColumn = 0; KoXmlDocument xmlDocument = SvgParser::createDocumentFromSvg(ba, &errorMsg, &errorLine, &errorColumn); if (xmlDocument.isNull()) { errorFlake << "Parsing error in " << filename() << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; errorFlake << "Parsing error in the main document at line" << errorLine << ", column" << errorColumn << endl << "Error message: " << errorMsg; return false; } KoDocumentResourceManager manager; SvgParser parser(&manager); parser.setResolution(QRectF(0,0,100,100), 72); // initialize with default values QSizeF fragmentSize; QList shapes = parser.parseSvg(xmlDocument.documentElement(), &fragmentSize); d->maskSize = fragmentSize; d->title = parser.documentTitle(); setName(d->title); d->description = parser.documentDescription(); setMaskShapes(shapes); if (store->open("preview.png")) { KoStoreDevice previewDev(store.data()); previewDev.open(QIODevice::ReadOnly); QImage preview = QImage(); preview.load(&previewDev, "PNG"); setImage(preview); (void)store->close(); } buf.close(); setValid(true); return true; } void KoGamutMask::setMaskShapes(QList shapes) { setMaskShapesToVector(shapes, d->maskShapes); } bool KoGamutMask::save() { QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } saveToDevice(&file); file.close(); return true; } QList KoGamutMask::koShapes() const { QList shapes; for(KoGamutMaskShape* maskShape: d->maskShapes) { shapes.append(maskShape->koShape()); } return shapes; } bool KoGamutMask::saveToDevice(QIODevice *dev) const { KoStore* store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-gamutmask", KoStore::Zip)); if (!store || store->bad()) return false; QList shapes = koShapes(); std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex); if (!store->open("gamutmask.svg")) { return false; } KoStoreDevice storeDev(store); storeDev.open(QIODevice::WriteOnly); SvgWriter writer(shapes); writer.setDocumentTitle(d->title); writer.setDocumentDescription(d->description); writer.save(storeDev, d->maskSize); if (!store->close()) { return false; } if (!store->open("preview.png")) { return false; } KoStoreDevice previewDev(store); previewDev.open(QIODevice::WriteOnly); image().save(&previewDev, "PNG"); if (!store->close()) { return false; } return store->finalize(); } QString KoGamutMask::title() { return d->title; } void KoGamutMask::setTitle(QString title) { d->title = title; setName(title); } QString KoGamutMask::description() { return d->description; } void KoGamutMask::setDescription(QString description) { d->description = description; } int KoGamutMask::rotation() { return d->rotation; } void KoGamutMask::setRotation(int rotation) { d->rotation = rotation; } QSizeF KoGamutMask::maskSize() { return d->maskSize; } void KoGamutMask::setPreviewMaskShapes(QList shapes) { setMaskShapesToVector(shapes, d->previewShapes); } void KoGamutMask::setMaskShapesToVector(QList shapes, QVector &targetVector) { targetVector.clear(); for(KoShape* sh: shapes) { KoGamutMaskShape* maskShape = new KoGamutMaskShape(sh); targetVector.append(maskShape); } } // clean up when ending mask preview void KoGamutMask::clearPreview() { d->previewShapes.clear(); } diff --git a/libs/flake/resources/KoGamutMask.h b/libs/flake/resources/KoGamutMask.h index 4ecf46ec6d..49936b2464 100644 --- a/libs/flake/resources/KoGamutMask.h +++ b/libs/flake/resources/KoGamutMask.h @@ -1,101 +1,102 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOGAMUTMASK_H #define KOGAMUTMASK_H #include #include #include #include #include #include #include #include #include class KoViewConverter; class KoGamutMaskShape { public: KoGamutMaskShape(KoShape* shape); KoGamutMaskShape(); ~KoGamutMaskShape(); bool coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter, int maskRotation) const; QPainterPath outline(); void paint(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation); void paintStroke(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation); KoShape* koShape(); private: KoShape* m_maskShape; KoShapePaintingContext m_shapePaintingContext; }; /** * @brief The resource type for gamut masks used by the artistic color selector */ class KRITAFLAKE_EXPORT KoGamutMask : public QObject, public KoResource { Q_OBJECT public: KoGamutMask(const QString &filename); KoGamutMask(); KoGamutMask(KoGamutMask *rhs); bool coordIsClear(const QPointF& coord, KoViewConverter& viewConverter, bool preview); bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; void paint(QPainter &painter, KoViewConverter& viewConverter, bool preview); void paintStroke(QPainter &painter, KoViewConverter& viewConverter, bool preview); QString title(); void setTitle(QString title); QString description(); void setDescription(QString description); int rotation(); void setRotation(int rotation); QSizeF maskSize(); void setMaskShapes(QList shapes); void setPreviewMaskShapes(QList shapes); QList koShapes() const; void clearPreview(); private: void setMaskShapesToVector(QList shapes, QVector& targetVector); struct Private; Private* const d; }; typedef QSharedPointer KoGamutMaskSP; #endif // KOGAMUTMASK_H diff --git a/libs/image/floodfill/kis_fill_interval_map.cpp b/libs/image/floodfill/kis_fill_interval_map.cpp index 3b93468ac5..05ea720160 100644 --- a/libs/image/floodfill/kis_fill_interval_map.cpp +++ b/libs/image/floodfill/kis_fill_interval_map.cpp @@ -1,166 +1,174 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_fill_interval_map.h" #include "kis_fill_interval_map_p.h" #include "kis_fill_sanity_checks.h" KisFillIntervalMap::KisFillIntervalMap() : m_d(new Private) { } KisFillIntervalMap::~KisFillIntervalMap() { } void KisFillIntervalMap::insertInterval(const KisFillInterval &interval) { Private::GlobalMap::iterator rowMap = m_d->map.find(interval.row); if (rowMap == m_d->map.end()) { rowMap = m_d->map.insert(interval.row, Private::LineIntervalMap()); } rowMap->insert(interval.start, interval); } void KisFillIntervalMap::cropInterval(KisFillInterval *interval) { Private::IteratorRange range; range = m_d->findFirstIntersectingInterval(*interval); Private::LineIntervalMap::iterator it = range.beginIt; while (interval->isValid() && it != range.endIt) { bool needsIncrement = true; if (it->start <= interval->start && it->end >= interval->start) { int savedIntervalStart = interval->start; interval->start = it->end + 1; /** * It might happen that we need to split a backward * interval into two pieces */ if (it->end > interval->end) { KisFillInterval newInterval(interval->end + 1, it->end, it->row); range.rowMapIt->insert(newInterval.start, newInterval); } it->end = savedIntervalStart - 1; /** * It might also happen that the backward interval is * fully eaten by the forward interval. This is possible * only in case the BW-interval was generated by the * strictly adjacent FW-interval, that is (it->start == * interval->start) */ if (!it->isValid()) { it = range.rowMapIt->erase(it); needsIncrement = false; } } else if (it->start <= interval->end && it->end >= interval->end) { int savedIntervalEnd = interval->end; interval->end = it->start - 1; it->start = savedIntervalEnd + 1; /** * The BW-interval is eaten by the FW-interval. See a * comment above */ if (!it->isValid()) { it = range.rowMapIt->erase(it); needsIncrement = false; } } else if (it->start > interval->end) { break; } #ifdef ENABLE_FILL_SANITY_CHECKS else if (it->start > interval->start && it->end < interval->end) { SANITY_ASSERT_MSG(0, "FATAL: The backward interval cannot fully reside inside the forward interval"); - it->invalidate(); interval->invalidate(); + it->invalidate(); + it = range.rowMapIt->erase(it); + needsIncrement = false; } - SANITY_ASSERT_MSG(it == range.endIt || it->isValid(), "FATAL: The backward interval cannot become invalid during the crop action"); - + // The code above should have removed the invalidated backward interval, + // just verify that + KIS_SAFE_ASSERT_RECOVER((it == range.endIt || it->isValid()) && + "FATAL: The backward interval cannot become " + "invalid during the crop action") { + it = range.rowMapIt->erase(it); + needsIncrement = false; + } #endif /* ENABLE_FILL_SANITY_CHECKS */ if (needsIncrement) { it++; } } } KisFillIntervalMap::Private::IteratorRange KisFillIntervalMap::Private::findFirstIntersectingInterval(const KisFillInterval &interval) { Private::GlobalMap::iterator rowMap = map.find(interval.row); if (rowMap == map.end()) { return IteratorRange(); } LineIntervalMap::iterator it = rowMap->begin(); LineIntervalMap::iterator end = rowMap->end(); while(it != end) { if (it->end < interval.start) { ++it; } else if (it->start > interval.end) { it = end; break; } else { break; } } return IteratorRange(it, end, rowMap); } QStack KisFillIntervalMap::fetchAllIntervals(int rowCorrection) const { QStack intervals; Private::GlobalMap::const_iterator rowMapIt = m_d->map.constBegin(); Private::GlobalMap::const_iterator rowMapEndIt = m_d->map.constEnd(); while (rowMapIt != rowMapEndIt) { Private::LineIntervalMap::const_iterator it = rowMapIt->constBegin(); Private::LineIntervalMap::const_iterator end = rowMapIt->constEnd(); while(it != end) { KisFillInterval interval = *it; interval.row += rowCorrection; intervals.append(interval); ++it; } ++rowMapIt; } return intervals; } void KisFillIntervalMap::clear() { m_d->map.clear(); } diff --git a/libs/image/floodfill/kis_fill_sanity_checks.h b/libs/image/floodfill/kis_fill_sanity_checks.h index 49d47efb12..2de42c9bda 100644 --- a/libs/image/floodfill/kis_fill_sanity_checks.h +++ b/libs/image/floodfill/kis_fill_sanity_checks.h @@ -1,41 +1,41 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_FILL_SANITY_CHECKS_H #define __KIS_FILL_SANITY_CHECKS_H #define ENABLE_FILL_SANITY_CHECKS //#define ENABLE_CHECKS_FOR_TESTING #ifdef ENABLE_FILL_SANITY_CHECKS #ifdef ENABLE_CHECKS_FOR_TESTING #include #define SANITY_ASSERT_MSG(cond, msg) ((!(cond)) ? throw std::invalid_argument(msg) : qt_noop()) #else -#define SANITY_ASSERT_MSG(cond, msg) Q_ASSERT((cond)) +#define SANITY_ASSERT_MSG(cond, msg) KIS_SAFE_ASSERT_RECOVER_NOOP((cond)) #endif /* ENABLE_CHECKS_FOR_TESTING */ #else #define SANITY_ASSERT_MSG(cond, msg) #endif /* ENABLE_FILL_SANITY_CHECKS */ #endif /* __KIS_FILL_SANITY_CHECKS_H */ diff --git a/libs/image/floodfill/kis_scanline_fill.cpp b/libs/image/floodfill/kis_scanline_fill.cpp index db91b1ce07..750d827c09 100644 --- a/libs/image/floodfill/kis_scanline_fill.cpp +++ b/libs/image/floodfill/kis_scanline_fill.cpp @@ -1,728 +1,732 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_scanline_fill.h" #include #include #include #include #include #include "kis_image.h" #include "kis_fill_interval_map.h" #include "kis_pixel_selection.h" #include "kis_random_accessor_ng.h" #include "kis_fill_sanity_checks.h" template class CopyToSelection : public BaseClass { public: typedef KisRandomConstAccessorSP SourceAccessorType; SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) { return device->createRandomConstAccessorNG(0, 0); } public: void setDestinationSelection(KisPaintDeviceSP pixelSelection) { m_pixelSelection = pixelSelection; m_it = m_pixelSelection->createRandomAccessorNG(0,0); } ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) { Q_UNUSED(dstPtr); m_it->moveTo(x, y); *m_it->rawData() = opacity; } private: KisPaintDeviceSP m_pixelSelection; KisRandomAccessorSP m_it; }; template class FillWithColor : public BaseClass { public: typedef KisRandomAccessorSP SourceAccessorType; SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) { return device->createRandomAccessorNG(0, 0); } public: FillWithColor() : m_pixelSize(0) {} void setFillColor(const KoColor &sourceColor) { m_sourceColor = sourceColor; m_pixelSize = sourceColor.colorSpace()->pixelSize(); m_data = m_sourceColor.data(); } ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) { Q_UNUSED(x); Q_UNUSED(y); if (opacity == MAX_SELECTED) { memcpy(dstPtr, m_data, m_pixelSize); } } private: KoColor m_sourceColor; const quint8 *m_data; int m_pixelSize; }; template class FillWithColorExternal : public BaseClass { public: typedef KisRandomConstAccessorSP SourceAccessorType; SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) { return device->createRandomConstAccessorNG(0, 0); } public: void setDestinationDevice(KisPaintDeviceSP device) { m_externalDevice = device; m_it = m_externalDevice->createRandomAccessorNG(0,0); } void setFillColor(const KoColor &sourceColor) { m_sourceColor = sourceColor; m_pixelSize = sourceColor.colorSpace()->pixelSize(); m_data = m_sourceColor.data(); } ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) { Q_UNUSED(dstPtr); m_it->moveTo(x, y); if (opacity == MAX_SELECTED) { memcpy(m_it->rawData(), m_data, m_pixelSize); } } private: KisPaintDeviceSP m_externalDevice; KisRandomAccessorSP m_it; KoColor m_sourceColor; const quint8 *m_data; int m_pixelSize; }; class DifferencePolicySlow { public: ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int threshold) { m_colorSpace = device->colorSpace(); m_srcPixel = srcPixel; m_srcPixelPtr = m_srcPixel.data(); m_threshold = threshold; } ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) { if (m_threshold == 1) { if (memcmp(m_srcPixelPtr, pixelPtr, m_colorSpace->pixelSize()) == 0) { return 0; } return quint8_MAX; } else { return m_colorSpace->difference(m_srcPixelPtr, pixelPtr); } } private: const KoColorSpace *m_colorSpace; KoColor m_srcPixel; const quint8 *m_srcPixelPtr; int m_threshold; }; template class DifferencePolicyOptimized { typedef SrcPixelType HashKeyType; typedef QHash HashType; public: ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int threshold) { m_colorSpace = device->colorSpace(); m_srcPixel = srcPixel; m_srcPixelPtr = m_srcPixel.data(); m_threshold = threshold; } ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) { HashKeyType key = *reinterpret_cast(pixelPtr); quint8 result; typename HashType::iterator it = m_differences.find(key); if (it != m_differences.end()) { result = *it; } else { if (m_threshold == 1) { if (memcmp(m_srcPixelPtr, pixelPtr, m_colorSpace->pixelSize()) == 0) { result = 0; } else { result = quint8_MAX; } } else { result = m_colorSpace->difference(m_srcPixelPtr, pixelPtr); } m_differences.insert(key, result); } return result; } private: HashType m_differences; const KoColorSpace *m_colorSpace; KoColor m_srcPixel; const quint8 *m_srcPixelPtr; int m_threshold; }; template class PixelFiller> class SelectionPolicy : public PixelFiller { public: typename PixelFiller::SourceAccessorType m_srcIt; public: SelectionPolicy(KisPaintDeviceSP device, const KoColor &srcPixel, int threshold) : m_threshold(threshold) { this->initDifferences(device, srcPixel, threshold); m_srcIt = this->createSourceDeviceAccessor(device); } ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr) { quint8 diff = this->calculateDifference(pixelPtr); if (!useSmoothSelection) { return diff <= m_threshold ? MAX_SELECTED : MIN_SELECTED; } else { quint8 selectionValue = qMax(0, m_threshold - diff); quint8 result = MIN_SELECTED; if (selectionValue > 0) { qreal selectionNorm = qreal(selectionValue) / m_threshold; result = MAX_SELECTED * selectionNorm; } return result; } } private: int m_threshold; }; class IsNonNullPolicySlow { public: ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int /*threshold*/) { Q_UNUSED(srcPixel); m_pixelSize = device->pixelSize(); m_testPixel.resize(m_pixelSize); } ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) { if (memcmp(m_testPixel.data(), pixelPtr, m_pixelSize) == 0) { return 0; } return quint8_MAX; } private: int m_pixelSize; QByteArray m_testPixel; }; template class IsNonNullPolicyOptimized { public: ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int /*threshold*/) { Q_UNUSED(device); Q_UNUSED(srcPixel); } ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) { SrcPixelType *pixel = reinterpret_cast(pixelPtr); return *pixel == 0; } }; class GroupSplitPolicy { public: typedef KisRandomAccessorSP SourceAccessorType; SourceAccessorType m_srcIt; public: GroupSplitPolicy(KisPaintDeviceSP scribbleDevice, KisPaintDeviceSP groupMapDevice, qint32 groupIndex, quint8 referenceValue, int threshold) : m_threshold(threshold), m_groupIndex(groupIndex), m_referenceValue(referenceValue) { KIS_SAFE_ASSERT_RECOVER_NOOP(m_groupIndex > 0); m_srcIt = scribbleDevice->createRandomAccessorNG(0,0); m_groupMapIt = groupMapDevice->createRandomAccessorNG(0,0); } ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr) { // TODO: either threshold should always be null, or there should be a special // case for *pixelPtr == 0, which is different from all the other groups, // whatever the threshold is int diff = qAbs(int(*pixelPtr) - m_referenceValue); return diff <= m_threshold ? MAX_SELECTED : MIN_SELECTED; } ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) { Q_UNUSED(opacity); // erase the scribble *dstPtr = 0; // write group index into the map m_groupMapIt->moveTo(x, y); qint32 *groupMapPtr = reinterpret_cast(m_groupMapIt->rawData()); if (*groupMapPtr != 0) { dbgImage << ppVar(*groupMapPtr) << ppVar(m_groupIndex); } KIS_SAFE_ASSERT_RECOVER_NOOP(*groupMapPtr == 0); *groupMapPtr = m_groupIndex; } private: int m_threshold; qint32 m_groupIndex; quint8 m_referenceValue; KisRandomAccessorSP m_groupMapIt; }; struct Q_DECL_HIDDEN KisScanlineFill::Private { KisPaintDeviceSP device; KisRandomAccessorSP it; QPoint startPoint; QRect boundingRect; int threshold; int rowIncrement; KisFillIntervalMap backwardMap; QStack forwardStack; inline void swapDirection() { rowIncrement *= -1; - SANITY_ASSERT_MSG(forwardStack.isEmpty(), - "FATAL: the forward stack must be empty " - "on a direction swap"); + KIS_SAFE_ASSERT_RECOVER_NOOP(forwardStack.isEmpty() && + "FATAL: the forward stack must be empty " + "on a direction swap"); forwardStack = QStack(backwardMap.fetchAllIntervals(rowIncrement)); backwardMap.clear(); } }; KisScanlineFill::KisScanlineFill(KisPaintDeviceSP device, const QPoint &startPoint, const QRect &boundingRect) : m_d(new Private) { m_d->device = device; m_d->it = device->createRandomAccessorNG(startPoint.x(), startPoint.y()); m_d->startPoint = startPoint; m_d->boundingRect = boundingRect; m_d->rowIncrement = 1; m_d->threshold = 0; } KisScanlineFill::~KisScanlineFill() { } void KisScanlineFill::setThreshold(int threshold) { m_d->threshold = threshold; } template void KisScanlineFill::extendedPass(KisFillInterval *currentInterval, int srcRow, bool extendRight, T &pixelPolicy) { int x; int endX; int columnIncrement; int *intervalBorder; int *backwardIntervalBorder; KisFillInterval backwardInterval(currentInterval->start, currentInterval->end, srcRow); if (extendRight) { x = currentInterval->end; endX = m_d->boundingRect.right(); if (x >= endX) return; columnIncrement = 1; intervalBorder = ¤tInterval->end; backwardInterval.start = currentInterval->end + 1; backwardIntervalBorder = &backwardInterval.end; } else { x = currentInterval->start; endX = m_d->boundingRect.left(); if (x <= endX) return; columnIncrement = -1; intervalBorder = ¤tInterval->start; backwardInterval.end = currentInterval->start - 1; backwardIntervalBorder = &backwardInterval.start; } do { x += columnIncrement; pixelPolicy.m_srcIt->moveTo(x, srcRow); quint8 *pixelPtr = const_cast(pixelPolicy.m_srcIt->rawDataConst()); // TODO: avoid doing const_cast quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr); if (opacity) { *intervalBorder = x; *backwardIntervalBorder = x; pixelPolicy.fillPixel(pixelPtr, opacity, x, srcRow); } else { break; } } while (x != endX); if (backwardInterval.isValid()) { m_d->backwardMap.insertInterval(backwardInterval); } } template void KisScanlineFill::processLine(KisFillInterval interval, const int rowIncrement, T &pixelPolicy) { m_d->backwardMap.cropInterval(&interval); if (!interval.isValid()) return; int firstX = interval.start; int lastX = interval.end; int x = firstX; int row = interval.row; int nextRow = row + rowIncrement; KisFillInterval currentForwardInterval; int numPixelsLeft = 0; quint8 *dataPtr = 0; const int pixelSize = m_d->device->pixelSize(); while(x <= lastX) { // a bit of optimzation for not calling slow random accessor // methods too often if (numPixelsLeft <= 0) { pixelPolicy.m_srcIt->moveTo(x, row); numPixelsLeft = pixelPolicy.m_srcIt->numContiguousColumns(x) - 1; dataPtr = const_cast(pixelPolicy.m_srcIt->rawDataConst()); } else { numPixelsLeft--; dataPtr += pixelSize; } quint8 *pixelPtr = dataPtr; quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr); if (opacity) { if (!currentForwardInterval.isValid()) { currentForwardInterval.start = x; currentForwardInterval.end = x; currentForwardInterval.row = nextRow; } else { currentForwardInterval.end = x; } pixelPolicy.fillPixel(pixelPtr, opacity, x, row); if (x == firstX) { extendedPass(¤tForwardInterval, row, false, pixelPolicy); } if (x == lastX) { extendedPass(¤tForwardInterval, row, true, pixelPolicy); } } else { if (currentForwardInterval.isValid()) { m_d->forwardStack.push(currentForwardInterval); currentForwardInterval.invalidate(); } } x++; } if (currentForwardInterval.isValid()) { m_d->forwardStack.push(currentForwardInterval); } } template void KisScanlineFill::runImpl(T &pixelPolicy) { KIS_ASSERT_RECOVER_RETURN(m_d->forwardStack.isEmpty()); KisFillInterval startInterval(m_d->startPoint.x(), m_d->startPoint.x(), m_d->startPoint.y()); m_d->forwardStack.push(startInterval); /** * In the end of the first pass we should add an interval * containing the starting pixel, but directed into the opposite * direction. We cannot do it in the very beginning because the * intervals are offset by 1 pixel during every swap operation. */ bool firstPass = true; while (!m_d->forwardStack.isEmpty()) { while (!m_d->forwardStack.isEmpty()) { KisFillInterval interval = m_d->forwardStack.pop(); if (interval.row > m_d->boundingRect.bottom() || interval.row < m_d->boundingRect.top()) { continue; } processLine(interval, m_d->rowIncrement, pixelPolicy); } m_d->swapDirection(); if (firstPass) { startInterval.row--; m_d->forwardStack.push(startInterval); firstPass = false; } } } -void KisScanlineFill::fillColor(const KoColor &fillColor) +void KisScanlineFill::fillColor(const KoColor &originalFillColor) { KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y()); KoColor srcColor(it->rawDataConst(), m_d->device->colorSpace()); + KoColor fillColor(originalFillColor); + fillColor.convertTo(m_d->device->colorSpace()); const int pixelSize = m_d->device->pixelSize(); if (pixelSize == 1) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(fillColor); runImpl(policy); } else if (pixelSize == 2) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(fillColor); runImpl(policy); } else if (pixelSize == 4) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(fillColor); runImpl(policy); } else if (pixelSize == 8) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(fillColor); runImpl(policy); } else { SelectionPolicy policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(fillColor); runImpl(policy); } } -void KisScanlineFill::fillColor(const KoColor &fillColor, KisPaintDeviceSP externalDevice) +void KisScanlineFill::fillColor(const KoColor &originalFillColor, KisPaintDeviceSP externalDevice) { KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y()); KoColor srcColor(it->rawDataConst(), m_d->device->colorSpace()); + KoColor fillColor(originalFillColor); + fillColor.convertTo(m_d->device->colorSpace()); const int pixelSize = m_d->device->pixelSize(); if (pixelSize == 1) { SelectionPolicy, FillWithColorExternal> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationDevice(externalDevice); policy.setFillColor(fillColor); runImpl(policy); } else if (pixelSize == 2) { SelectionPolicy, FillWithColorExternal> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationDevice(externalDevice); policy.setFillColor(fillColor); runImpl(policy); } else if (pixelSize == 4) { SelectionPolicy, FillWithColorExternal> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationDevice(externalDevice); policy.setFillColor(fillColor); runImpl(policy); } else if (pixelSize == 8) { SelectionPolicy, FillWithColorExternal> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationDevice(externalDevice); policy.setFillColor(fillColor); runImpl(policy); } else { SelectionPolicy policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationDevice(externalDevice); policy.setFillColor(fillColor); runImpl(policy); } } void KisScanlineFill::fillSelection(KisPixelSelectionSP pixelSelection) { KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y()); KoColor srcColor(it->rawDataConst(), m_d->device->colorSpace()); const int pixelSize = m_d->device->pixelSize(); if (pixelSize == 1) { SelectionPolicy, CopyToSelection> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationSelection(pixelSelection); runImpl(policy); } else if (pixelSize == 2) { SelectionPolicy, CopyToSelection> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationSelection(pixelSelection); runImpl(policy); } else if (pixelSize == 4) { SelectionPolicy, CopyToSelection> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationSelection(pixelSelection); runImpl(policy); } else if (pixelSize == 8) { SelectionPolicy, CopyToSelection> policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationSelection(pixelSelection); runImpl(policy); } else { SelectionPolicy policy(m_d->device, srcColor, m_d->threshold); policy.setDestinationSelection(pixelSelection); runImpl(policy); } } void KisScanlineFill::clearNonZeroComponent() { const int pixelSize = m_d->device->pixelSize(); KoColor srcColor(Qt::transparent, m_d->device->colorSpace()); if (pixelSize == 1) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(srcColor); runImpl(policy); } else if (pixelSize == 2) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(srcColor); runImpl(policy); } else if (pixelSize == 4) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(srcColor); runImpl(policy); } else if (pixelSize == 8) { SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(srcColor); runImpl(policy); } else { SelectionPolicy policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(srcColor); runImpl(policy); } } void KisScanlineFill::fillContiguousGroup(KisPaintDeviceSP groupMapDevice, qint32 groupIndex) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->device->pixelSize() == 1); KIS_SAFE_ASSERT_RECOVER_RETURN(groupMapDevice->pixelSize() == 4); KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y()); const quint8 referenceValue = *it->rawDataConst(); GroupSplitPolicy policy(m_d->device, groupMapDevice, groupIndex, referenceValue, m_d->threshold); runImpl(policy); } void KisScanlineFill::testingProcessLine(const KisFillInterval &processInterval) { KoColor srcColor(QColor(0,0,0,0), m_d->device->colorSpace()); KoColor fillColor(QColor(200,200,200,200), m_d->device->colorSpace()); SelectionPolicy, FillWithColor> policy(m_d->device, srcColor, m_d->threshold); policy.setFillColor(fillColor); processLine(processInterval, 1, policy); } QVector KisScanlineFill::testingGetForwardIntervals() const { return QVector(m_d->forwardStack); } KisFillIntervalMap* KisScanlineFill::testingGetBackwardIntervals() const { return &m_d->backwardMap; } diff --git a/libs/image/kis_layer_utils.cpp b/libs/image/kis_layer_utils.cpp index 4935b75bb7..ba7e7cab6b 100644 --- a/libs/image/kis_layer_utils.cpp +++ b/libs/image/kis_layer_utils.cpp @@ -1,1556 +1,1557 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_utils.h" #include #include #include #include #include "kis_painter.h" #include "kis_image.h" #include "kis_node.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_clone_layer.h" #include "kis_group_layer.h" #include "kis_selection.h" #include "kis_selection_mask.h" #include "kis_meta_data_merge_strategy.h" #include #include "commands/kis_image_layer_add_command.h" #include "commands/kis_image_layer_remove_command.h" #include "commands/kis_image_layer_move_command.h" #include "commands/kis_image_change_layers_command.h" #include "commands_new/kis_activate_selection_mask_command.h" #include "commands/kis_image_change_visibility_command.h" #include "kis_abstract_projection_plane.h" #include "kis_processing_applicator.h" #include "kis_image_animation_interface.h" #include "kis_keyframe_channel.h" #include "kis_command_utils.h" #include "commands_new/kis_change_projection_color_command.h" #include "kis_layer_properties_icons.h" #include "lazybrush/kis_colorize_mask.h" #include "commands/kis_node_property_list_command.h" #include "commands/kis_node_compositeop_command.h" #include #include "krita_utils.h" #include "kis_image_signal_router.h" namespace KisLayerUtils { void fetchSelectionMasks(KisNodeList mergedNodes, QVector &selectionMasks) { foreach (KisNodeSP node, mergedNodes) { - KisLayerSP layer = qobject_cast(node.data()); - KisSelectionMaskSP mask; + Q_FOREACH(KisNodeSP child, node->childNodes(QStringList("KisSelectionMask"), KoProperties())) { - if (layer && (mask = layer->selectionMask())) { - selectionMasks.append(mask); + KisSelectionMaskSP mask = qobject_cast(child.data()); + if (mask) { + selectionMasks.append(mask); + } } } } struct MergeDownInfoBase { MergeDownInfoBase(KisImageSP _image) : image(_image), storage(new SwitchFrameCommand::SharedStorage()) { } virtual ~MergeDownInfoBase() {} KisImageWSP image; QVector selectionMasks; KisNodeSP dstNode; SwitchFrameCommand::SharedStorageSP storage; QSet frames; bool useInTimeline = false; bool enableOnionSkins = false; virtual KisNodeList allSrcNodes() = 0; KisLayerSP dstLayer() { return qobject_cast(dstNode.data()); } }; struct MergeDownInfo : public MergeDownInfoBase { MergeDownInfo(KisImageSP _image, KisLayerSP _prevLayer, KisLayerSP _currLayer) : MergeDownInfoBase(_image), prevLayer(_prevLayer), currLayer(_currLayer) { frames = fetchLayerFramesRecursive(prevLayer) | fetchLayerFramesRecursive(currLayer); useInTimeline = prevLayer->useInTimeline() || currLayer->useInTimeline(); const KisPaintLayer *paintLayer = qobject_cast(currLayer.data()); if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled(); paintLayer = qobject_cast(prevLayer.data()); if (paintLayer) enableOnionSkins |= paintLayer->onionSkinEnabled(); } KisLayerSP prevLayer; KisLayerSP currLayer; KisNodeList allSrcNodes() override { KisNodeList mergedNodes; mergedNodes << currLayer; mergedNodes << prevLayer; return mergedNodes; } }; struct MergeMultipleInfo : public MergeDownInfoBase { MergeMultipleInfo(KisImageSP _image, KisNodeList _mergedNodes) : MergeDownInfoBase(_image), mergedNodes(_mergedNodes) { foreach (KisNodeSP node, mergedNodes) { frames |= fetchLayerFramesRecursive(node); useInTimeline |= node->useInTimeline(); const KisPaintLayer *paintLayer = qobject_cast(node.data()); if (paintLayer) { enableOnionSkins |= paintLayer->onionSkinEnabled(); } } } KisNodeList mergedNodes; bool nodesCompositingVaries = false; KisNodeList allSrcNodes() override { return mergedNodes; } }; typedef QSharedPointer MergeDownInfoBaseSP; typedef QSharedPointer MergeDownInfoSP; typedef QSharedPointer MergeMultipleInfoSP; struct FillSelectionMasks : public KUndo2Command { FillSelectionMasks(MergeDownInfoBaseSP info) : m_info(info) {} void redo() override { fetchSelectionMasks(m_info->allSrcNodes(), m_info->selectionMasks); } private: MergeDownInfoBaseSP m_info; }; struct DisableColorizeKeyStrokes : public KisCommandUtils::AggregateCommand { DisableColorizeKeyStrokes(MergeDownInfoBaseSP info) : m_info(info) {} void populateChildCommands() override { Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) { recursiveApplyNodes(node, [this] (KisNodeSP node) { if (dynamic_cast(node.data()) && KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool()) { KisBaseNode::PropertyList props = node->sectionModelProperties(); KisLayerPropertiesIcons::setNodeProperty(&props, KisLayerPropertiesIcons::colorizeEditKeyStrokes, false); addCommand(new KisNodePropertyListCommand(node, props)); } }); } } private: MergeDownInfoBaseSP m_info; }; struct DisableOnionSkins : public KisCommandUtils::AggregateCommand { DisableOnionSkins(MergeDownInfoBaseSP info) : m_info(info) {} void populateChildCommands() override { Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) { recursiveApplyNodes(node, [this] (KisNodeSP node) { if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::onionSkins, false).toBool()) { KisBaseNode::PropertyList props = node->sectionModelProperties(); KisLayerPropertiesIcons::setNodeProperty(&props, KisLayerPropertiesIcons::onionSkins, false); addCommand(new KisNodePropertyListCommand(node, props)); } }); } } private: MergeDownInfoBaseSP m_info; }; struct DisableExtraCompositing : public KisCommandUtils::AggregateCommand { DisableExtraCompositing(MergeMultipleInfoSP info) : m_info(info) {} void populateChildCommands() override { /** * We disable extra compositing only in case all the layers have * the same compositing properties, therefore, we can just sum them using * Normal blend mode */ if (m_info->nodesCompositingVaries) return; // we should disable dirty requests on **redo only**, otherwise // the state of the layers will not be recovered on undo m_info->image->disableDirtyRequests(); Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) { if (node->compositeOpId() != COMPOSITE_OVER) { addCommand(new KisNodeCompositeOpCommand(node, node->compositeOpId(), COMPOSITE_OVER)); } if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::inheritAlpha, false).toBool()) { KisBaseNode::PropertyList props = node->sectionModelProperties(); KisLayerPropertiesIcons::setNodeProperty(&props, KisLayerPropertiesIcons::inheritAlpha, false); addCommand(new KisNodePropertyListCommand(node, props)); } } m_info->image->enableDirtyRequests(); } private: MergeMultipleInfoSP m_info; }; struct DisablePassThroughForHeadsOnly : public KisCommandUtils::AggregateCommand { DisablePassThroughForHeadsOnly(MergeDownInfoBaseSP info, bool skipIfDstIsGroup = false) : m_info(info), m_skipIfDstIsGroup(skipIfDstIsGroup) { } void populateChildCommands() override { if (m_skipIfDstIsGroup && m_info->dstLayer() && m_info->dstLayer()->inherits("KisGroupLayer")) { return; } Q_FOREACH (KisNodeSP node, m_info->allSrcNodes()) { if (KisLayerPropertiesIcons::nodeProperty(node, KisLayerPropertiesIcons::passThrough, false).toBool()) { KisBaseNode::PropertyList props = node->sectionModelProperties(); KisLayerPropertiesIcons::setNodeProperty(&props, KisLayerPropertiesIcons::passThrough, false); addCommand(new KisNodePropertyListCommand(node, props)); } } } private: MergeDownInfoBaseSP m_info; bool m_skipIfDstIsGroup; }; struct RefreshHiddenAreas : public KUndo2Command { RefreshHiddenAreas(MergeDownInfoBaseSP info) : m_info(info) {} void redo() override { KisImageAnimationInterface *interface = m_info->image->animationInterface(); const QRect preparedRect = !interface->externalFrameActive() ? m_info->image->bounds() : QRect(); foreach (KisNodeSP node, m_info->allSrcNodes()) { refreshHiddenAreaAsync(node, preparedRect); } } private: QRect realNodeExactBounds(KisNodeSP rootNode, QRect currentRect = QRect()) { KisNodeSP node = rootNode->firstChild(); while(node) { currentRect |= realNodeExactBounds(node, currentRect); node = node->nextSibling(); } // TODO: it would be better to count up changeRect inside // node's extent() method currentRect |= rootNode->projectionPlane()->changeRect(rootNode->exactBounds()); return currentRect; } void refreshHiddenAreaAsync(KisNodeSP rootNode, const QRect &preparedArea) { QRect realNodeRect = realNodeExactBounds(rootNode); if (!preparedArea.contains(realNodeRect)) { QRegion dirtyRegion = realNodeRect; dirtyRegion -= preparedArea; foreach(const QRect &rc, dirtyRegion.rects()) { m_info->image->refreshGraphAsync(rootNode, rc, realNodeRect); } } } private: MergeDownInfoBaseSP m_info; }; struct RefreshDelayedUpdateLayers : public KUndo2Command { RefreshDelayedUpdateLayers(MergeDownInfoBaseSP info) : m_info(info) {} void redo() override { foreach (KisNodeSP node, m_info->allSrcNodes()) { forceAllDelayedNodesUpdate(node); } } private: MergeDownInfoBaseSP m_info; }; struct KeepMergedNodesSelected : public KisCommandUtils::AggregateCommand { KeepMergedNodesSelected(MergeDownInfoSP info, bool finalizing) : m_singleInfo(info), m_finalizing(finalizing) {} KeepMergedNodesSelected(MergeMultipleInfoSP info, KisNodeSP putAfter, bool finalizing) : m_multipleInfo(info), m_finalizing(finalizing), m_putAfter(putAfter) {} void populateChildCommands() override { KisNodeSP prevNode; KisNodeSP nextNode; KisNodeList prevSelection; KisNodeList nextSelection; KisImageSP image; if (m_singleInfo) { prevNode = m_singleInfo->currLayer; nextNode = m_singleInfo->dstNode; image = m_singleInfo->image; } else if (m_multipleInfo) { prevNode = m_putAfter; nextNode = m_multipleInfo->dstNode; prevSelection = m_multipleInfo->allSrcNodes(); image = m_multipleInfo->image; } if (!m_finalizing) { addCommand(new KeepNodesSelectedCommand(prevSelection, KisNodeList(), prevNode, KisNodeSP(), image, false)); } else { addCommand(new KeepNodesSelectedCommand(KisNodeList(), nextSelection, KisNodeSP(), nextNode, image, true)); } } private: MergeDownInfoSP m_singleInfo; MergeMultipleInfoSP m_multipleInfo; bool m_finalizing; KisNodeSP m_putAfter; }; struct CreateMergedLayer : public KisCommandUtils::AggregateCommand { CreateMergedLayer(MergeDownInfoSP info) : m_info(info) {} void populateChildCommands() override { // actual merging done by KisLayer::createMergedLayer (or specialized descendant) m_info->dstNode = m_info->currLayer->createMergedLayerTemplate(m_info->prevLayer); if (m_info->frames.size() > 0) { m_info->dstNode->enableAnimation(); m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true); } m_info->dstNode->setUseInTimeline(m_info->useInTimeline); KisPaintLayer *dstPaintLayer = qobject_cast(m_info->dstNode.data()); if (dstPaintLayer) { dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins); } } private: MergeDownInfoSP m_info; }; struct CreateMergedLayerMultiple : public KisCommandUtils::AggregateCommand { CreateMergedLayerMultiple(MergeMultipleInfoSP info, const QString name = QString() ) : m_info(info), m_name(name) {} void populateChildCommands() override { QString mergedLayerName; if (m_name.isEmpty()){ const QString mergedLayerSuffix = i18n("Merged"); mergedLayerName = m_info->mergedNodes.first()->name(); if (!mergedLayerName.endsWith(mergedLayerSuffix)) { mergedLayerName = QString("%1 %2") .arg(mergedLayerName).arg(mergedLayerSuffix); } } else { mergedLayerName = m_name; } KisPaintLayer *dstPaintLayer = new KisPaintLayer(m_info->image, mergedLayerName, OPACITY_OPAQUE_U8); m_info->dstNode = dstPaintLayer; if (m_info->frames.size() > 0) { m_info->dstNode->enableAnimation(); m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id(), true); } auto channelFlagsLazy = [](KisNodeSP node) { KisLayer *layer = dynamic_cast(node.data()); return layer ? layer->channelFlags() : QBitArray(); }; QString compositeOpId; QBitArray channelFlags; bool compositionVaries = false; bool isFirstCycle = true; foreach (KisNodeSP node, m_info->allSrcNodes()) { if (isFirstCycle) { compositeOpId = node->compositeOpId(); channelFlags = channelFlagsLazy(node); isFirstCycle = false; } else if (compositeOpId != node->compositeOpId() || channelFlags != channelFlagsLazy(node)) { compositionVaries = true; break; } KisLayerSP layer = qobject_cast(node.data()); if (layer && layer->layerStyle()) { compositionVaries = true; break; } } if (!compositionVaries) { if (!compositeOpId.isEmpty()) { m_info->dstNode->setCompositeOpId(compositeOpId); } if (m_info->dstLayer() && !channelFlags.isEmpty()) { m_info->dstLayer()->setChannelFlags(channelFlags); } } m_info->nodesCompositingVaries = compositionVaries; m_info->dstNode->setUseInTimeline(m_info->useInTimeline); dstPaintLayer->setOnionSkinEnabled(m_info->enableOnionSkins); } private: MergeMultipleInfoSP m_info; QString m_name; }; struct MergeLayers : public KisCommandUtils::AggregateCommand { MergeLayers(MergeDownInfoSP info) : m_info(info) {} void populateChildCommands() override { // actual merging done by KisLayer::createMergedLayer (or specialized descendant) m_info->currLayer->fillMergedLayerTemplate(m_info->dstLayer(), m_info->prevLayer); } private: MergeDownInfoSP m_info; }; struct MergeLayersMultiple : public KisCommandUtils::AggregateCommand { MergeLayersMultiple(MergeMultipleInfoSP info) : m_info(info) {} void populateChildCommands() override { KisPainter gc(m_info->dstNode->paintDevice()); foreach (KisNodeSP node, m_info->allSrcNodes()) { QRect rc = node->exactBounds() | m_info->image->bounds(); node->projectionPlane()->apply(&gc, rc); } } private: MergeMultipleInfoSP m_info; }; struct MergeMetaData : public KUndo2Command { MergeMetaData(MergeDownInfoSP info, const KisMetaData::MergeStrategy* strategy) : m_info(info), m_strategy(strategy) {} void redo() override { QRect layerProjectionExtent = m_info->currLayer->projection()->extent(); QRect prevLayerProjectionExtent = m_info->prevLayer->projection()->extent(); int prevLayerArea = prevLayerProjectionExtent.width() * prevLayerProjectionExtent.height(); int layerArea = layerProjectionExtent.width() * layerProjectionExtent.height(); QList scores; double norm = qMax(prevLayerArea, layerArea); scores.append(prevLayerArea / norm); scores.append(layerArea / norm); QList srcs; srcs.append(m_info->prevLayer->metaData()); srcs.append(m_info->currLayer->metaData()); m_strategy->merge(m_info->dstLayer()->metaData(), srcs, scores); } private: MergeDownInfoSP m_info; const KisMetaData::MergeStrategy *m_strategy; }; KeepNodesSelectedCommand::KeepNodesSelectedCommand(const KisNodeList &selectedBefore, const KisNodeList &selectedAfter, KisNodeSP activeBefore, KisNodeSP activeAfter, KisImageSP image, bool finalize, KUndo2Command *parent) : FlipFlopCommand(finalize, parent), m_selectedBefore(selectedBefore), m_selectedAfter(selectedAfter), m_activeBefore(activeBefore), m_activeAfter(activeAfter), m_image(image) { } void KeepNodesSelectedCommand::partB() { KisImageSignalType type; if (getState() == State::FINALIZING) { type = ComplexNodeReselectionSignal(m_activeAfter, m_selectedAfter); } else { type = ComplexNodeReselectionSignal(m_activeBefore, m_selectedBefore); } m_image->signalRouter()->emitNotification(type); } SelectGlobalSelectionMask::SelectGlobalSelectionMask(KisImageSP image) : m_image(image) { } void SelectGlobalSelectionMask::redo() { KisImageSignalType type = ComplexNodeReselectionSignal(m_image->rootLayer()->selectionMask(), KisNodeList()); m_image->signalRouter()->emitNotification(type); } RemoveNodeHelper::~RemoveNodeHelper() { } /** * The removal of two nodes in one go may be a bit tricky, because one * of them may be the clone of another. If we remove the source of a * clone layer, it will reincarnate into a paint layer. In this case * the pointer to the second layer will be lost. * * That's why we need to care about the order of the nodes removal: * the clone --- first, the source --- last. */ void RemoveNodeHelper::safeRemoveMultipleNodes(KisNodeList nodes, KisImageSP image) { const bool lastLayer = scanForLastLayer(image, nodes); auto isNodeWeird = [] (KisNodeSP node) { const bool normalCompositeMode = node->compositeOpId() == COMPOSITE_OVER; KisLayer *layer = dynamic_cast(node.data()); const bool hasInheritAlpha = layer && layer->alphaChannelDisabled(); return !normalCompositeMode && !hasInheritAlpha; }; while (!nodes.isEmpty()) { KisNodeList::iterator it = nodes.begin(); while (it != nodes.end()) { if (!checkIsSourceForClone(*it, nodes)) { KisNodeSP node = *it; addCommandImpl(new KisImageLayerRemoveCommand(image, node, !isNodeWeird(node), true)); it = nodes.erase(it); } else { ++it; } } } if (lastLayer) { KisLayerSP newLayer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace()); addCommandImpl(new KisImageLayerAddCommand(image, newLayer, image->root(), KisNodeSP(), false, false)); } } bool RemoveNodeHelper::checkIsSourceForClone(KisNodeSP src, const KisNodeList &nodes) { foreach (KisNodeSP node, nodes) { if (node == src) continue; KisCloneLayer *clone = dynamic_cast(node.data()); if (clone && KisNodeSP(clone->copyFrom()) == src) { return true; } } return false; } bool RemoveNodeHelper::scanForLastLayer(KisImageWSP image, KisNodeList nodesToRemove) { bool removeLayers = false; Q_FOREACH(KisNodeSP nodeToRemove, nodesToRemove) { if (qobject_cast(nodeToRemove.data())) { removeLayers = true; break; } } if (!removeLayers) return false; bool lastLayer = true; KisNodeSP node = image->root()->firstChild(); while (node) { if (!nodesToRemove.contains(node) && qobject_cast(node.data()) && !node->isFakeNode()) { lastLayer = false; break; } node = node->nextSibling(); } return lastLayer; } SimpleRemoveLayers::SimpleRemoveLayers(const KisNodeList &nodes, KisImageSP image) : m_nodes(nodes), m_image(image) { } void SimpleRemoveLayers::populateChildCommands() { if (m_nodes.isEmpty()) return; safeRemoveMultipleNodes(m_nodes, m_image); } void SimpleRemoveLayers::addCommandImpl(KUndo2Command *cmd) { addCommand(cmd); } struct InsertNode : public KisCommandUtils::AggregateCommand { InsertNode(MergeDownInfoBaseSP info, KisNodeSP putAfter) : m_info(info), m_putAfter(putAfter) {} void populateChildCommands() override { addCommand(new KisImageLayerAddCommand(m_info->image, m_info->dstNode, m_putAfter->parent(), m_putAfter, true, false)); } private: virtual void addCommandImpl(KUndo2Command *cmd) { addCommand(cmd); } private: MergeDownInfoBaseSP m_info; KisNodeSP m_putAfter; }; struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils::AggregateCommand { CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter) : m_info(info), m_putAfter(putAfter) {} static void findPerfectParent(KisNodeList nodesToDelete, KisNodeSP &putAfter, KisNodeSP &parent) { if (!putAfter) { putAfter = nodesToDelete.last(); } // Add the new merged node on top of the active node // -- checking all parents if they are included in nodesToDelete // Not every descendant is included in nodesToDelete even if in fact // they are going to be deleted, so we need to check it. // If we consider the path from root to the putAfter node, // if there are any nodes marked for deletion, any node afterwards // is going to be deleted, too. // example: root . . . . . ! ! . . ! ! ! ! . . . . putAfter // it should be: root . . . . . ! ! ! ! ! ! ! ! ! ! ! ! !putAfter // and here: root . . . . X ! ! . . ! ! ! ! . . . . putAfter // you can see which node is "the perfect ancestor" // (marked X; called "parent" in the function arguments). // and here: root . . . . . O ! . . ! ! ! ! . . . . putAfter // you can see which node is "the topmost deleted ancestor" (marked 'O') KisNodeSP node = putAfter->parent(); bool foundDeletedAncestor = false; KisNodeSP topmostAncestorToDelete = nullptr; while (node) { if (nodesToDelete.contains(node) && !nodesToDelete.contains(node->parent())) { foundDeletedAncestor = true; topmostAncestorToDelete = node; // Here node is to be deleted and its parent is not, // so its parent is the one of the first not deleted (="perfect") ancestors. // We need the one that is closest to the top (root) } node = node->parent(); } if (foundDeletedAncestor) { parent = topmostAncestorToDelete->parent(); putAfter = topmostAncestorToDelete; } else { parent = putAfter->parent(); // putAfter (and none of its ancestors) is to be deleted, so its parent is the first not deleted ancestor } } void populateChildCommands() override { KisNodeList nodesToDelete = m_info->allSrcNodes(); KisNodeSP parent; findPerfectParent(nodesToDelete, m_putAfter, parent); if (!parent) { KisNodeSP oldRoot = m_info->image->root(); KisNodeSP newRoot(new KisGroupLayer(m_info->image, "root", OPACITY_OPAQUE_U8)); addCommand(new KisImageLayerAddCommand(m_info->image, m_info->dstNode, newRoot, KisNodeSP(), true, false)); addCommand(new KisImageChangeLayersCommand(m_info->image, oldRoot, newRoot)); } else { addCommand(new KisImageLayerAddCommand(m_info->image, m_info->dstNode, parent, m_putAfter, true, false)); /** * We can merge selection masks, in this case dstLayer is not defined! */ if (m_info->dstLayer()) { reparentSelectionMasks(m_info->image, m_info->dstLayer(), m_info->selectionMasks); } KisNodeList safeNodesToDelete = m_info->allSrcNodes(); for (KisNodeList::iterator it = safeNodesToDelete.begin(); it != safeNodesToDelete.end(); ++it) { KisNodeSP node = *it; if (node->userLocked() && node->visible()) { addCommand(new KisImageChangeVisibilityCommand(false, node)); } } KritaUtils::filterContainer(safeNodesToDelete, [](KisNodeSP node) { return !node->userLocked(); }); safeRemoveMultipleNodes(safeNodesToDelete, m_info->image); } } private: void addCommandImpl(KUndo2Command *cmd) override { addCommand(cmd); } void reparentSelectionMasks(KisImageSP image, KisLayerSP newLayer, const QVector &selectionMasks) { KIS_SAFE_ASSERT_RECOVER_RETURN(newLayer); foreach (KisSelectionMaskSP mask, selectionMasks) { addCommand(new KisImageLayerMoveCommand(image, mask, newLayer, newLayer->lastChild())); addCommand(new KisActivateSelectionMaskCommand(mask, false)); } } private: MergeDownInfoBaseSP m_info; KisNodeSP m_putAfter; }; SwitchFrameCommand::SharedStorage::~SharedStorage() { } SwitchFrameCommand::SwitchFrameCommand(KisImageSP image, int time, bool finalize, SharedStorageSP storage) : FlipFlopCommand(finalize), m_image(image), m_newTime(time), m_storage(storage) {} SwitchFrameCommand::~SwitchFrameCommand() {} void SwitchFrameCommand::partA() { KisImageAnimationInterface *interface = m_image->animationInterface(); const int currentTime = interface->currentTime(); if (currentTime == m_newTime) { m_storage->value = m_newTime; return; } interface->image()->disableUIUpdates(); interface->saveAndResetCurrentTime(m_newTime, &m_storage->value); } void SwitchFrameCommand::partB() { KisImageAnimationInterface *interface = m_image->animationInterface(); const int currentTime = interface->currentTime(); if (currentTime == m_storage->value) { return; } interface->restoreCurrentTime(&m_storage->value); interface->image()->enableUIUpdates(); } struct AddNewFrame : public KisCommandUtils::AggregateCommand { AddNewFrame(MergeDownInfoBaseSP info, int frame) : m_info(info), m_frame(frame) {} void populateChildCommands() override { KUndo2Command *cmd = new KisCommandUtils::SkipFirstRedoWrapper(); KisKeyframeChannel *channel = m_info->dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id()); KisKeyframeSP keyframe = channel->addKeyframe(m_frame, cmd); applyKeyframeColorLabel(keyframe); addCommand(cmd); } void applyKeyframeColorLabel(KisKeyframeSP dstKeyframe) { Q_FOREACH(KisNodeSP srcNode, m_info->allSrcNodes()) { Q_FOREACH(KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) { KisKeyframeSP keyframe = channel->keyframeAt(m_frame); if (!keyframe.isNull() && keyframe->colorLabel() != 0) { dstKeyframe->setColorLabel(keyframe->colorLabel()); return; } } } dstKeyframe->setColorLabel(0); } private: MergeDownInfoBaseSP m_info; int m_frame; }; QSet fetchLayerFrames(KisNodeSP node) { KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (!channel) return QSet(); return channel->allKeyframeIds(); } QSet fetchLayerFramesRecursive(KisNodeSP rootNode) { QSet frames = fetchLayerFrames(rootNode); KisNodeSP node = rootNode->firstChild(); while(node) { frames |= fetchLayerFramesRecursive(node); node = node->nextSibling(); } return frames; } void updateFrameJobs(FrameJobs *jobs, KisNodeSP node) { QSet frames = fetchLayerFrames(node); if (frames.isEmpty()) { (*jobs)[0].insert(node); } else { foreach (int frame, frames) { (*jobs)[frame].insert(node); } } } void updateFrameJobsRecursive(FrameJobs *jobs, KisNodeSP rootNode) { updateFrameJobs(jobs, rootNode); KisNodeSP node = rootNode->firstChild(); while(node) { updateFrameJobsRecursive(jobs, node); node = node->nextSibling(); } } void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy) { if (!layer->prevSibling()) return; // XXX: this breaks if we allow free mixing of masks and layers KisLayerSP prevLayer = qobject_cast(layer->prevSibling().data()); if (!prevLayer) return; if (!layer->visible() && !prevLayer->visible()) { return; } KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Merge Down")); if (layer->visible() && prevLayer->visible()) { MergeDownInfoSP info(new MergeDownInfo(image, prevLayer, layer)); // disable key strokes on all colorize masks, all onion skins on // paint layers and wait until update is finished with a barrier applicator.applyCommand(new DisableColorizeKeyStrokes(info)); applicator.applyCommand(new DisableOnionSkins(info)); applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER); applicator.applyCommand(new KeepMergedNodesSelected(info, false)); applicator.applyCommand(new FillSelectionMasks(info)); applicator.applyCommand(new CreateMergedLayer(info), KisStrokeJobData::BARRIER); // NOTE: shape layer may have emitted spontaneous jobs during layer creation, // wait for them to complete! applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER); // in two-layer mode we disable pass through only when the destination layer // is not a group layer applicator.applyCommand(new DisablePassThroughForHeadsOnly(info, true)); applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER); if (info->frames.size() > 0) { foreach (int frame, info->frames) { applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage)); applicator.applyCommand(new AddNewFrame(info, frame)); applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage)); } } else { applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new MergeLayers(info), KisStrokeJobData::BARRIER); } applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER); applicator.applyCommand(new CleanUpNodes(info, layer), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KeepMergedNodesSelected(info, true)); } else if (layer->visible()) { applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), layer, KisNodeSP(), image, false)); applicator.applyCommand( new SimpleRemoveLayers(KisNodeList() << prevLayer, image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), KisNodeSP(), layer, image, true)); } else if (prevLayer->visible()) { applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), layer, KisNodeSP(), image, false)); applicator.applyCommand( new SimpleRemoveLayers(KisNodeList() << layer, image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KeepNodesSelectedCommand(KisNodeList(), KisNodeList(), KisNodeSP(), prevLayer, image, true)); } applicator.end(); } bool checkIsChildOf(KisNodeSP node, const KisNodeList &parents) { KisNodeList nodeParents; KisNodeSP parent = node->parent(); while (parent) { nodeParents << parent; parent = parent->parent(); } foreach(KisNodeSP perspectiveParent, parents) { if (nodeParents.contains(perspectiveParent)) { return true; } } return false; } bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes) { bool result = false; KisCloneLayer *clone = dynamic_cast(node.data()); if (clone) { KisNodeSP cloneSource = KisNodeSP(clone->copyFrom()); Q_FOREACH(KisNodeSP subtree, nodes) { result = recursiveFindNode(subtree, [cloneSource](KisNodeSP node) -> bool { return node == cloneSource; }); if (!result) { result = checkIsCloneOf(cloneSource, nodes); } if (result) { break; } } } return result; } void filterMergableNodes(KisNodeList &nodes, bool allowMasks) { KisNodeList::iterator it = nodes.begin(); while (it != nodes.end()) { if ((!allowMasks && !qobject_cast(it->data())) || checkIsChildOf(*it, nodes)) { //qDebug() << "Skipping node" << ppVar((*it)->name()); it = nodes.erase(it); } else { ++it; } } } void sortMergableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes) { KisNodeList::iterator it = std::find(inputNodes.begin(), inputNodes.end(), root); if (it != inputNodes.end()) { outputNodes << *it; inputNodes.erase(it); } if (inputNodes.isEmpty()) { return; } KisNodeSP child = root->firstChild(); while (child) { sortMergableNodes(child, inputNodes, outputNodes); child = child->nextSibling(); } /** * By the end of recursion \p inputNodes must be empty */ KIS_ASSERT_RECOVER_NOOP(root->parent() || inputNodes.isEmpty()); } KisNodeList sortMergableNodes(KisNodeSP root, KisNodeList nodes) { KisNodeList result; sortMergableNodes(root, nodes, result); return result; } KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks) { KIS_ASSERT_RECOVER(!nodes.isEmpty()) { return nodes; } KisNodeSP root; Q_FOREACH(KisNodeSP node, nodes) { KisNodeSP localRoot = node; while (localRoot->parent()) { localRoot = localRoot->parent(); } if (!root) { root = localRoot; } KIS_ASSERT_RECOVER(root == localRoot) { return nodes; } } KisNodeList result; sortMergableNodes(root, nodes, result); filterMergableNodes(result, allowMasks); return result; } void addCopyOfNameTag(KisNodeSP node) { const QString prefix = i18n("Copy of"); QString newName = node->name(); if (!newName.startsWith(prefix)) { newName = QString("%1 %2").arg(prefix).arg(newName); node->setName(newName); } } KisNodeList findNodesWithProps(KisNodeSP root, const KoProperties &props, bool excludeRoot) { KisNodeList nodes; if ((!excludeRoot || root->parent()) && root->check(props)) { nodes << root; } KisNodeSP node = root->firstChild(); while (node) { nodes += findNodesWithProps(node, props, excludeRoot); node = node->nextSibling(); } return nodes; } KisNodeList filterInvisibleNodes(const KisNodeList &nodes, KisNodeList *invisibleNodes, KisNodeSP *putAfter) { KIS_ASSERT_RECOVER(invisibleNodes) { return nodes; } KIS_ASSERT_RECOVER(putAfter) { return nodes; } KisNodeList visibleNodes; int putAfterIndex = -1; Q_FOREACH(KisNodeSP node, nodes) { if (node->visible() || node->userLocked()) { visibleNodes << node; } else { *invisibleNodes << node; if (node == *putAfter) { putAfterIndex = visibleNodes.size() - 1; } } } if (!visibleNodes.isEmpty() && putAfterIndex >= 0) { putAfterIndex = qBound(0, putAfterIndex, visibleNodes.size() - 1); *putAfter = visibleNodes[putAfterIndex]; } return visibleNodes; } void filterUnlockedNodes(KisNodeList &nodes) { KisNodeList::iterator it = nodes.begin(); while (it != nodes.end()) { if ((*it)->userLocked()) { it = nodes.erase(it); } else { ++it; } } } void changeImageDefaultProjectionColor(KisImageSP image, const KoColor &color) { KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(image, image->root(), KisProcessingApplicator::RECURSIVE, emitSignals, kundo2_i18n("Change projection color"), 0, 142857 + 1); applicator.applyCommand(new KisChangeProjectionColorCommand(image, color), KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); applicator.end(); } void mergeMultipleLayersImpl(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter, bool flattenSingleLayer, const KUndo2MagicString &actionName, bool cleanupNodes = true, const QString layerName = QString()) { if (!putAfter) { putAfter = mergedNodes.first(); } filterMergableNodes(mergedNodes); { KisNodeList tempNodes; std::swap(mergedNodes, tempNodes); sortMergableNodes(image->root(), tempNodes, mergedNodes); } if (mergedNodes.size() <= 1 && (!flattenSingleLayer && mergedNodes.size() == 1)) return; KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes); KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, actionName); KisNodeList originalNodes = mergedNodes; KisNodeList invisibleNodes; mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter); if (!invisibleNodes.isEmpty()) { /* If the putAfter node is invisible, * we should instead pick one of the nodes * to be merged to avoid a null putAfter. */ if (!putAfter->visible()){ putAfter = mergedNodes.first(); } applicator.applyCommand( new SimpleRemoveLayers(invisibleNodes, image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } if (mergedNodes.size() > 1 || invisibleNodes.isEmpty()) { MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes)); // disable key strokes on all colorize masks, all onion skins on // paint layers and wait until update is finished with a barrier applicator.applyCommand(new DisableColorizeKeyStrokes(info)); applicator.applyCommand(new DisableOnionSkins(info)); applicator.applyCommand(new DisablePassThroughForHeadsOnly(info)); applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER); applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, false)); applicator.applyCommand(new FillSelectionMasks(info)); applicator.applyCommand(new CreateMergedLayerMultiple(info, layerName), KisStrokeJobData::BARRIER); applicator.applyCommand(new DisableExtraCompositing(info)); applicator.applyCommand(new KUndo2Command(), KisStrokeJobData::BARRIER); if (info->frames.size() > 0) { foreach (int frame, info->frames) { applicator.applyCommand(new SwitchFrameCommand(info->image, frame, false, info->storage)); applicator.applyCommand(new AddNewFrame(info, frame)); applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new SwitchFrameCommand(info->image, frame, true, info->storage)); } } else { applicator.applyCommand(new RefreshHiddenAreas(info)); applicator.applyCommand(new RefreshDelayedUpdateLayers(info), KisStrokeJobData::BARRIER); applicator.applyCommand(new MergeLayersMultiple(info), KisStrokeJobData::BARRIER); } //applicator.applyCommand(new MergeMetaData(info, strategy), KisStrokeJobData::BARRIER); if (cleanupNodes){ applicator.applyCommand(new CleanUpNodes(info, putAfter), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } else { applicator.applyCommand(new InsertNode(info, putAfter), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } applicator.applyCommand(new KeepMergedNodesSelected(info, putAfter, true)); } applicator.end(); } void mergeMultipleLayers(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter) { mergeMultipleLayersImpl(image, mergedNodes, putAfter, false, kundo2_i18n("Merge Selected Nodes")); } void newLayerFromVisible(KisImageSP image, KisNodeSP putAfter) { KisNodeList mergedNodes; mergedNodes << image->root(); mergeMultipleLayersImpl(image, mergedNodes, putAfter, true, kundo2_i18n("New From Visible"), false, i18nc("New layer created from all the visible layers", "Visible")); } struct MergeSelectionMasks : public KisCommandUtils::AggregateCommand { MergeSelectionMasks(MergeDownInfoBaseSP info, KisNodeSP putAfter) : m_info(info), m_putAfter(putAfter){} void populateChildCommands() override { KisNodeSP parent; CleanUpNodes::findPerfectParent(m_info->allSrcNodes(), m_putAfter, parent); KisLayerSP parentLayer; do { parentLayer = qobject_cast(parent.data()); parent = parent->parent(); } while(!parentLayer && parent); KisSelectionSP selection = new KisSelection(); foreach (KisNodeSP node, m_info->allSrcNodes()) { KisMaskSP mask = dynamic_cast(node.data()); if (!mask) continue; selection->pixelSelection()->applySelection( mask->selection()->pixelSelection(), SELECTION_ADD); } KisSelectionMaskSP mergedMask = new KisSelectionMask(m_info->image); mergedMask->initSelection(parentLayer); mergedMask->setSelection(selection); m_info->dstNode = mergedMask; } private: MergeDownInfoBaseSP m_info; KisNodeSP m_putAfter; }; struct ActivateSelectionMask : public KisCommandUtils::AggregateCommand { ActivateSelectionMask(MergeDownInfoBaseSP info) : m_info(info) {} void populateChildCommands() override { KisSelectionMaskSP mergedMask = dynamic_cast(m_info->dstNode.data()); addCommand(new KisActivateSelectionMaskCommand(mergedMask, true)); } private: MergeDownInfoBaseSP m_info; }; bool tryMergeSelectionMasks(KisImageSP image, KisNodeList mergedNodes, KisNodeSP putAfter) { QList selectionMasks; for (auto it = mergedNodes.begin(); it != mergedNodes.end(); /*noop*/) { KisSelectionMaskSP mask = dynamic_cast(it->data()); if (!mask) { it = mergedNodes.erase(it); } else { selectionMasks.append(mask); ++it; } } if (mergedNodes.isEmpty()) return false; KisLayerSP parentLayer = qobject_cast(selectionMasks.first()->parent().data()); KIS_ASSERT_RECOVER(parentLayer) { return 0; } KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; KisProcessingApplicator applicator(image, 0, KisProcessingApplicator::NONE, emitSignals, kundo2_i18n("Merge Selection Masks")); MergeMultipleInfoSP info(new MergeMultipleInfo(image, mergedNodes)); applicator.applyCommand(new MergeSelectionMasks(info, putAfter)); applicator.applyCommand(new CleanUpNodes(info, putAfter), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new ActivateSelectionMask(info)); applicator.end(); return true; } void flattenLayer(KisImageSP image, KisLayerSP layer) { if (!layer->childCount() && !layer->layerStyle()) return; KisNodeList mergedNodes; mergedNodes << layer; mergeMultipleLayersImpl(image, mergedNodes, layer, true, kundo2_i18n("Flatten Layer")); } void flattenImage(KisImageSP image, KisNodeSP activeNode) { if (!activeNode) { activeNode = image->root()->lastChild(); } KisNodeList mergedNodes; mergedNodes << image->root(); mergeMultipleLayersImpl(image, mergedNodes, activeNode, true, kundo2_i18n("Flatten Image")); } KisSimpleUpdateCommand::KisSimpleUpdateCommand(KisNodeList nodes, bool finalize, KUndo2Command *parent) : FlipFlopCommand(finalize, parent), m_nodes(nodes) { } void KisSimpleUpdateCommand::partB() { updateNodes(m_nodes); } void KisSimpleUpdateCommand::updateNodes(const KisNodeList &nodes) { Q_FOREACH(KisNodeSP node, nodes) { node->setDirty(node->extent()); } } KisNodeSP recursiveFindNode(KisNodeSP node, std::function func) { if (func(node)) { return node; } node = node->firstChild(); while (node) { KisNodeSP resultNode = recursiveFindNode(node, func); if (resultNode) { return resultNode; } node = node->nextSibling(); } return 0; } KisNodeSP findNodeByUuid(KisNodeSP root, const QUuid &uuid) { return recursiveFindNode(root, [uuid] (KisNodeSP node) { return node->uuid() == uuid; }); } void forceAllDelayedNodesUpdate(KisNodeSP root) { KisLayerUtils::recursiveApplyNodes(root, [] (KisNodeSP node) { KisDelayedUpdateNodeInterface *delayedUpdate = dynamic_cast(node.data()); if (delayedUpdate) { delayedUpdate->forceUpdateTimedNode(); } }); } bool hasDelayedNodeWithUpdates(KisNodeSP root) { return recursiveFindNode(root, [] (KisNodeSP node) { KisDelayedUpdateNodeInterface *delayedUpdate = dynamic_cast(node.data()); return delayedUpdate ? delayedUpdate->hasPendingTimedUpdates() : false; }); } KisImageSP findImageByHierarchy(KisNodeSP node) { while (node) { const KisLayer *layer = dynamic_cast(node.data()); if (layer) { return layer->image(); } node = node->parent(); } return 0; } } diff --git a/libs/image/kis_perspectivetransform_worker.cpp b/libs/image/kis_perspectivetransform_worker.cpp index 514e0ecac5..adfa76ed03 100644 --- a/libs/image/kis_perspectivetransform_worker.cpp +++ b/libs/image/kis_perspectivetransform_worker.cpp @@ -1,190 +1,192 @@ /* * This file is part of Krita * * Copyright (c) 2006 Cyrille Berger * Copyright (c) 2009 Edward Apap * Copyright (c) 2010 Marc Pegon * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + #include "kis_perspectivetransform_worker.h" #include #include #include #include #include #include #include #include "kis_paint_device.h" #include "kis_perspective_math.h" #include "kis_random_accessor_ng.h" #include "kis_random_sub_accessor.h" #include "kis_selection.h" #include #include "krita_utils.h" #include "kis_progress_update_helper.h" #include "kis_painter.h" #include "kis_image.h" KisPerspectiveTransformWorker::KisPerspectiveTransformWorker(KisPaintDeviceSP dev, QPointF center, double aX, double aY, double distance, KoUpdaterPtr progress) : m_dev(dev), m_progressUpdater(progress) { QMatrix4x4 m; m.rotate(180. * aX / M_PI, QVector3D(1, 0, 0)); m.rotate(180. * aY / M_PI, QVector3D(0, 1, 0)); QTransform project = m.toTransform(distance); QTransform t = QTransform::fromTranslate(center.x(), center.y()); QTransform forwardTransform = t.inverted() * project * t; init(forwardTransform); } KisPerspectiveTransformWorker::KisPerspectiveTransformWorker(KisPaintDeviceSP dev, const QTransform &transform, KoUpdaterPtr progress) : m_dev(dev), m_progressUpdater(progress) { init(transform); } void KisPerspectiveTransformWorker::fillParams(const QRectF &srcRect, const QRect &dstBaseClipRect, QRegion *dstRegion, QPolygonF *dstClipPolygon) { QPolygonF bounds = srcRect; QPolygonF newBounds = m_forwardTransform.map(bounds); newBounds = newBounds.intersected(QRectF(dstBaseClipRect)); QPainterPath path; path.addPolygon(newBounds); *dstRegion = KritaUtils::splitPath(path); *dstClipPolygon = newBounds; } void KisPerspectiveTransformWorker::init(const QTransform &transform) { m_isIdentity = transform.isIdentity(); m_forwardTransform = transform; m_backwardTransform = transform.inverted(); if (m_dev) { m_srcRect = m_dev->exactBounds(); QPolygonF dstClipPolygonUnused; fillParams(m_srcRect, m_dev->defaultBounds()->bounds(), &m_dstRegion, &dstClipPolygonUnused); } } KisPerspectiveTransformWorker::~KisPerspectiveTransformWorker() { } void KisPerspectiveTransformWorker::setForwardTransform(const QTransform &transform) { init(transform); } void KisPerspectiveTransformWorker::run() { KIS_ASSERT_RECOVER_RETURN(m_dev); if (m_isIdentity) return; KisPaintDeviceSP cloneDevice = new KisPaintDevice(*m_dev.data()); // Clear the destination device, since all the tiles are already // shared with cloneDevice m_dev->clear(); KIS_ASSERT_RECOVER_NOOP(!m_isIdentity); KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, m_dstRegion.rectCount()); KisRandomSubAccessorSP srcAcc = cloneDevice->createRandomSubAccessor(); KisRandomAccessorSP accessor = m_dev->createRandomAccessorNG(0, 0); Q_FOREACH (const QRect &rect, m_dstRegion.rects()) { for (int y = rect.y(); y < rect.y() + rect.height(); ++y) { for (int x = rect.x(); x < rect.x() + rect.width(); ++x) { QPointF dstPoint(x, y); QPointF srcPoint = m_backwardTransform.map(dstPoint); if (m_srcRect.contains(srcPoint)) { accessor->moveTo(dstPoint.x(), dstPoint.y()); srcAcc->moveTo(srcPoint.x(), srcPoint.y()); srcAcc->sampledOldRawData(accessor->rawData()); } } } progressHelper.step(); } } void KisPerspectiveTransformWorker::runPartialDst(KisPaintDeviceSP srcDev, KisPaintDeviceSP dstDev, const QRect &dstRect) { if (m_isIdentity) { KisPainter::copyAreaOptimizedOldData(dstRect.topLeft(), srcDev, dstDev, dstRect); return; } QRectF srcClipRect = srcDev->exactBounds(); if (srcClipRect.isEmpty()) return; KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, dstRect.height()); KisRandomSubAccessorSP srcAcc = srcDev->createRandomSubAccessor(); KisRandomAccessorSP accessor = dstDev->createRandomAccessorNG(dstRect.x(), dstRect.y()); for (int y = dstRect.y(); y < dstRect.y() + dstRect.height(); ++y) { for (int x = dstRect.x(); x < dstRect.x() + dstRect.width(); ++x) { QPointF dstPoint(x, y); QPointF srcPoint = m_backwardTransform.map(dstPoint); if (srcClipRect.contains(srcPoint)) { accessor->moveTo(dstPoint.x(), dstPoint.y()); srcAcc->moveTo(srcPoint.x(), srcPoint.y()); srcAcc->sampledOldRawData(accessor->rawData()); } } progressHelper.step(); } } QTransform KisPerspectiveTransformWorker::forwardTransform() const { return m_forwardTransform; } QTransform KisPerspectiveTransformWorker::backwardTransform() const { return m_backwardTransform; } diff --git a/libs/image/kis_perspectivetransform_worker.h b/libs/image/kis_perspectivetransform_worker.h index f148ea61a2..c80697fbd1 100644 --- a/libs/image/kis_perspectivetransform_worker.h +++ b/libs/image/kis_perspectivetransform_worker.h @@ -1,69 +1,68 @@ /* - * This file is part of Krita - * * Copyright (c) 2006 Cyrille Berger * Copyright (c) 2010 Marc Pegon * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PERSPECTIVETRANSFORM_WORKER_H #define KIS_PERSPECTIVETRANSFORM_WORKER_H #include "kis_types.h" #include "kritaimage_export.h" #include #include #include #include class KRITAIMAGE_EXPORT KisPerspectiveTransformWorker { public: KisPerspectiveTransformWorker(KisPaintDeviceSP dev, QPointF center, double aX, double aY, double distance, KoUpdaterPtr progress); KisPerspectiveTransformWorker(KisPaintDeviceSP dev, const QTransform &transform, KoUpdaterPtr progress); ~KisPerspectiveTransformWorker(); void run(); void runPartialDst(KisPaintDeviceSP srcDev, KisPaintDeviceSP dstDev, const QRect &dstRect); void setForwardTransform(const QTransform &transform); QTransform forwardTransform() const; QTransform backwardTransform() const; private: void init(const QTransform &transform); void fillParams(const QRectF &srcRect, const QRect &dstBaseClipRect, QRegion *dstRegion, QPolygonF *dstClipPolygon); private: KisPaintDeviceSP m_dev; KoUpdaterPtr m_progressUpdater; QRegion m_dstRegion; QRectF m_srcRect; QTransform m_backwardTransform; QTransform m_forwardTransform; bool m_isIdentity; }; #endif diff --git a/libs/image/kis_random_sub_accessor.cpp b/libs/image/kis_random_sub_accessor.cpp index 08ae4128cb..9a0dfa87fa 100644 --- a/libs/image/kis_random_sub_accessor.cpp +++ b/libs/image/kis_random_sub_accessor.cpp @@ -1,92 +1,109 @@ /* * This file is part of the KDE project * * Copyright (c) 2006 Cyrille Berger * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_random_sub_accessor.h" #include #include #include #include #include "kis_paint_device.h" KisRandomSubAccessor::KisRandomSubAccessor(KisPaintDeviceSP device) : m_device(device) , m_currentPoint(0, 0) , m_randomAccessor(device->createRandomConstAccessorNG(0, 0)) { } KisRandomSubAccessor::~KisRandomSubAccessor() { } void KisRandomSubAccessor::sampledOldRawData(quint8* dst) { const quint8* pixels[4]; qint16 weights[4]; - int x = (int)floor(m_currentPoint.x()); - int y = (int)floor(m_currentPoint.y()); + qreal x = m_currentPoint.x(); + qreal y = m_currentPoint.y(); + int xz = qRound(x); + int yz = qRound(y); + double hsub = m_currentPoint.x() - x; - if (hsub < 0.0) hsub = 1.0 + hsub; + if (hsub < 0.0) { + hsub = 1.0 + hsub; + } double vsub = m_currentPoint.y() - y; - if (vsub < 0.0) vsub = 1.0 + vsub; + if (vsub < 0.0) { + vsub = 1.0 + vsub; + } + weights[0] = qRound((1.0 - hsub) * (1.0 - vsub) * 255); - m_randomAccessor->moveTo(x, y); + m_randomAccessor->moveTo(xz, yz); pixels[0] = m_randomAccessor->oldRawData(); weights[1] = qRound((1.0 - vsub) * hsub * 255); - m_randomAccessor->moveTo(x + 1, y); + m_randomAccessor->moveTo(xz + 1, yz); pixels[1] = m_randomAccessor->oldRawData(); weights[2] = qRound(vsub * (1.0 - hsub) * 255); - m_randomAccessor->moveTo(x, y + 1); + m_randomAccessor->moveTo(xz, yz + 1); pixels[2] = m_randomAccessor->oldRawData(); weights[3] = qRound(hsub * vsub * 255); - m_randomAccessor->moveTo(x + 1, y + 1); + m_randomAccessor->moveTo(xz + 1, yz + 1); pixels[3] = m_randomAccessor->oldRawData(); + m_device->colorSpace()->mixColorsOp()->mixColors(pixels, weights, 4, dst); } void KisRandomSubAccessor::sampledRawData(quint8* dst) { const quint8* pixels[4]; qint16 weights[4]; - int x = (int)floor(m_currentPoint.x()); - int y = (int)floor(m_currentPoint.y()); + qreal x = m_currentPoint.x(); + qreal y = m_currentPoint.y(); + int xz = qRound(x); + int yz = qRound(y); + double hsub = m_currentPoint.x() - x; - if (hsub < 0.0) hsub = 1.0 + hsub; + if (hsub < 0.0) { + hsub = 1.0 + hsub; + } double vsub = m_currentPoint.y() - y; - if (vsub < 0.0) vsub = 1.0 + vsub; + if (vsub < 0.0) { + vsub = 1.0 + vsub; + } + weights[0] = qRound((1.0 - hsub) * (1.0 - vsub) * 255); - m_randomAccessor->moveTo(x, y); + m_randomAccessor->moveTo(xz, yz); pixels[0] = m_randomAccessor->rawDataConst(); weights[1] = qRound((1.0 - vsub) * hsub * 255); - m_randomAccessor->moveTo(x + 1, y); + m_randomAccessor->moveTo(xz + 1, yz); pixels[1] = m_randomAccessor->rawDataConst(); weights[2] = qRound(vsub * (1.0 - hsub) * 255); - m_randomAccessor->moveTo(x, y + 1); + m_randomAccessor->moveTo(xz, yz + 1); pixels[2] = m_randomAccessor->rawDataConst(); weights[3] = qRound(hsub * vsub * 255); - m_randomAccessor->moveTo(x + 1, y + 1); + m_randomAccessor->moveTo(xz + 1, yz + 1); pixels[3] = m_randomAccessor->rawDataConst(); m_device->colorSpace()->mixColorsOp()->mixColors(pixels, weights, 4, dst); } diff --git a/libs/image/kis_random_sub_accessor.h b/libs/image/kis_random_sub_accessor.h index 2b3cca8c63..b3b81e1810 100644 --- a/libs/image/kis_random_sub_accessor.h +++ b/libs/image/kis_random_sub_accessor.h @@ -1,62 +1,63 @@ /* * This file is part of the KDE project * * Copyright (c) 2006 Cyrille Berger * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_RANDOM_SUB_ACCESSOR_H #define KIS_RANDOM_SUB_ACCESSOR_H #include "kis_random_accessor_ng.h" #include "kis_types.h" #include #include "kis_shared.h" /** * Gives a random access to the sampled subpixels of an image. Use the * moveTo function to select the pixel. And then rawData to access the * value of a pixel. */ class KRITAIMAGE_EXPORT KisRandomSubAccessor : public KisShared { public: KisRandomSubAccessor(KisPaintDeviceSP device); ~KisRandomSubAccessor(); /** * Copy the sampled old value to destination */ void sampledOldRawData(quint8* dst); /** * Copy the sampled value to destination */ void sampledRawData(quint8* dst); - inline void moveTo(double x, double y) { + inline void moveTo(qreal x, qreal y) { m_currentPoint.setX(x); m_currentPoint.setY(y); } inline void moveTo(const QPointF& p) { m_currentPoint = p; } + private: KisPaintDeviceSP m_device; QPointF m_currentPoint; KisRandomConstAccessorSP m_randomAccessor; }; #endif diff --git a/libs/image/tiles3/kis_tile_hash_table2.h b/libs/image/tiles3/kis_tile_hash_table2.h index 1a4d34c454..7372f7ccfe 100644 --- a/libs/image/tiles3/kis_tile_hash_table2.h +++ b/libs/image/tiles3/kis_tile_hash_table2.h @@ -1,478 +1,478 @@ /* * Copyright (c) 2018 Andrey Kamakin * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TILEHASHTABLE_2_H #define KIS_TILEHASHTABLE_2_H #include "kis_shared.h" #include "kis_shared_ptr.h" #include "3rdparty/lock_free_map/concurrent_map.h" #include "kis_tile.h" #include "kis_debug.h" #define SANITY_CHECK /** * This is a template for a hash table that stores tiles (or some other * objects resembling tiles). Actually, this object should only have * col()/row() methods and be able to answer notifyDetachedFromDataManager() requests to * be stored here. It is used in KisTiledDataManager and * KisMementoManager. * * How to use: * 1) each hash must be unique, otherwise tiles would rewrite each-other * 2) 0 key is reserved, so can't be used * 3) col and row must be less than 0x7FFF to guarantee uniqueness of hash for each pair */ template class KisTileHashTableIteratorTraits2; template class KisTileHashTableTraits2 { static constexpr bool isInherited = std::is_convertible::value; Q_STATIC_ASSERT_X(isInherited, "Template must inherit KisShared"); public: typedef T TileType; typedef KisSharedPtr TileTypeSP; typedef KisWeakSharedPtr TileTypeWSP; KisTileHashTableTraits2(KisMementoManager *mm); KisTileHashTableTraits2(const KisTileHashTableTraits2 &ht, KisMementoManager *mm); ~KisTileHashTableTraits2(); bool isEmpty() { return !m_numTiles.load(); } bool tileExists(qint32 col, qint32 row); /** * Returns a tile in position (col,row). If no tile exists, * returns null. * \param col column of the tile * \param row row of the tile */ TileTypeSP getExistingTile(qint32 col, qint32 row); /** * Returns a tile in position (col,row). If no tile exists, * creates a new one, attaches it to the list and returns. * \param col column of the tile * \param row row of the tile * \param newTile out-parameter, returns true if a new tile * was created */ TileTypeSP getTileLazy(qint32 col, qint32 row, bool& newTile); /** * Returns a tile in position (col,row). If no tile exists, * creates nothing, but returns shared default tile object * of the table. Be careful, this object has column and row * parameters set to (qint32_MIN, qint32_MIN). * \param col column of the tile * \param row row of the tile * \param existingTile returns true if the tile actually exists in the table * and it is not a lazily created default wrapper tile */ TileTypeSP getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile); void addTile(TileTypeSP tile); bool deleteTile(TileTypeSP tile); bool deleteTile(qint32 col, qint32 row); void clear(); void setDefaultTileData(KisTileData *defaultTileData); KisTileData* defaultTileData(); qint32 numTiles() { return m_numTiles.load(); } void debugPrintInfo(); void debugMaxListLength(qint32 &min, qint32 &max); friend class KisTileHashTableIteratorTraits2; private: struct MemoryReclaimer { MemoryReclaimer(TileType *data) : d(data) {} void destroy() { TileTypeSP::deref(reinterpret_cast(this), d); delete this; } private: TileType *d; }; inline quint32 calculateHash(qint32 col, qint32 row) { #ifdef SANITY_CHECK - KIS_ASSERT_RECOVER_NOOP(row < 0x7FFF && col < 0x7FFF) + KIS_SAFE_ASSERT_RECOVER_NOOP(row < 0x7FFF && col < 0x7FFF) #endif // SANITY_CHECK if (col == 0 && row == 0) { col = 0x7FFF; row = 0x7FFF; } return ((static_cast(row) << 16) | (static_cast(col) & 0xFFFF)); } inline void insert(quint32 idx, TileTypeSP item) { TileTypeSP::ref(&item, item.data()); TileType *tile = 0; { QReadLocker locker(&m_iteratorLock); m_map.getGC().lockRawPointerAccess(); tile = m_map.assign(idx, item.data()); } if (tile) { tile->notifyDeadWithoutDetaching(); m_map.getGC().enqueue(&MemoryReclaimer::destroy, new MemoryReclaimer(tile)); } else { m_numTiles.fetchAndAddRelaxed(1); } m_map.getGC().unlockRawPointerAccess(); m_map.getGC().update(m_map.migrationInProcess()); } inline bool erase(quint32 idx) { m_map.getGC().lockRawPointerAccess(); bool wasDeleted = false; TileType *tile = m_map.erase(idx); if (tile) { tile->notifyDetachedFromDataManager(); wasDeleted = true; m_numTiles.fetchAndSubRelaxed(1); m_map.getGC().enqueue(&MemoryReclaimer::destroy, new MemoryReclaimer(tile)); } m_map.getGC().unlockRawPointerAccess(); m_map.getGC().update(m_map.migrationInProcess()); return wasDeleted; } private: typedef ConcurrentMap LockFreeTileMap; typedef typename LockFreeTileMap::Mutator LockFreeTileMapMutator; mutable LockFreeTileMap m_map; /** * We still need something to guard changes in m_defaultTileData, * otherwise there will be concurrent read/writes, resulting in broken memory. */ QReadWriteLock m_defaultPixelDataLock; mutable QReadWriteLock m_iteratorLock; QAtomicInt m_numTiles; KisTileData *m_defaultTileData; KisMementoManager *m_mementoManager; }; template class KisTileHashTableIteratorTraits2 { public: typedef T TileType; typedef KisSharedPtr TileTypeSP; typedef typename ConcurrentMap::Iterator Iterator; KisTileHashTableIteratorTraits2(KisTileHashTableTraits2 *ht) : m_ht(ht) { m_ht->m_iteratorLock.lockForWrite(); m_iter.setMap(m_ht->m_map); } ~KisTileHashTableIteratorTraits2() { m_ht->m_iteratorLock.unlock(); } void next() { m_iter.next(); } TileTypeSP tile() const { return TileTypeSP(m_iter.getValue()); } bool isDone() const { return !m_iter.isValid(); } void deleteCurrent() { m_ht->erase(m_iter.getKey()); next(); } void moveCurrentToHashTable(KisTileHashTableTraits2 *newHashTable) { TileTypeSP tile = m_iter.getValue(); next(); quint32 idx = m_ht->calculateHash(tile->col(), tile->row()); m_ht->erase(idx); newHashTable->insert(idx, tile); } private: KisTileHashTableTraits2 *m_ht; Iterator m_iter; }; template KisTileHashTableTraits2::KisTileHashTableTraits2(KisMementoManager *mm) : m_numTiles(0), m_defaultTileData(0), m_mementoManager(mm) { } template KisTileHashTableTraits2::KisTileHashTableTraits2(const KisTileHashTableTraits2 &ht, KisMementoManager *mm) : KisTileHashTableTraits2(mm) { setDefaultTileData(ht.m_defaultTileData); QWriteLocker locker(&ht.m_iteratorLock); typename ConcurrentMap::Iterator iter(ht.m_map); while (iter.isValid()) { TileTypeSP tile = new TileType(*iter.getValue(), m_mementoManager); insert(iter.getKey(), tile); iter.next(); } } template KisTileHashTableTraits2::~KisTileHashTableTraits2() { clear(); setDefaultTileData(0); } template bool KisTileHashTableTraits2::tileExists(qint32 col, qint32 row) { return getExistingTile(col, row); } template typename KisTileHashTableTraits2::TileTypeSP KisTileHashTableTraits2::getExistingTile(qint32 col, qint32 row) { quint32 idx = calculateHash(col, row); m_map.getGC().lockRawPointerAccess(); TileTypeSP tile = m_map.get(idx); m_map.getGC().unlockRawPointerAccess(); m_map.getGC().update(m_map.migrationInProcess()); return tile; } template typename KisTileHashTableTraits2::TileTypeSP KisTileHashTableTraits2::getTileLazy(qint32 col, qint32 row, bool &newTile) { newTile = false; quint32 idx = calculateHash(col, row); // we are going to assign a raw-pointer tile from the table // to a shared pointer... m_map.getGC().lockRawPointerAccess(); TileTypeSP tile; while (!(tile = m_map.get(idx))) { LockFreeTileMapMutator mutator = m_map.insertOrFind(idx); if (!mutator.getValue()) { // we shouldn't try to aquire **any** lock with // raw-pointer lock held m_map.getGC().unlockRawPointerAccess(); { QReadLocker locker(&m_defaultPixelDataLock); tile = new TileType(col, row, m_defaultTileData, 0); } TileTypeSP::ref(&tile, tile.data()); TileType *item = 0; // iterator lock should be taken **before** // the pointers are locked m_iteratorLock.lockForRead(); // and now lock raw-pointers again m_map.getGC().lockRawPointerAccess(); item = mutator.exchangeValue(tile.data()); m_iteratorLock.unlock(); if (item) { // we've got our tile back, it didn't manage to // get into the table. Now release the allocated // tile and push TO/GA switch. tile = 0; item->notifyDeadWithoutDetaching(); m_map.getGC().enqueue(&MemoryReclaimer::destroy, new MemoryReclaimer(item)); continue; } else { newTile = true; m_numTiles.fetchAndAddRelaxed(1); tile->notifyAttachedToDataManager(m_mementoManager); } } else { tile = mutator.getValue(); } } m_map.getGC().unlockRawPointerAccess(); m_map.getGC().update(m_map.migrationInProcess()); return tile; } template typename KisTileHashTableTraits2::TileTypeSP KisTileHashTableTraits2::getReadOnlyTileLazy(qint32 col, qint32 row, bool &existingTile) { quint32 idx = calculateHash(col, row); m_map.getGC().lockRawPointerAccess(); TileTypeSP tile = m_map.get(idx); m_map.getGC().unlockRawPointerAccess(); existingTile = tile; if (!existingTile) { QReadLocker locker(&m_defaultPixelDataLock); tile = new TileType(col, row, m_defaultTileData, 0); } m_map.getGC().update(m_map.migrationInProcess()); return tile; } template void KisTileHashTableTraits2::addTile(TileTypeSP tile) { quint32 idx = calculateHash(tile->col(), tile->row()); insert(idx, tile); } template bool KisTileHashTableTraits2::deleteTile(TileTypeSP tile) { return deleteTile(tile->col(), tile->row()); } template bool KisTileHashTableTraits2::deleteTile(qint32 col, qint32 row) { quint32 idx = calculateHash(col, row); return erase(idx); } template void KisTileHashTableTraits2::clear() { { QWriteLocker locker(&m_iteratorLock); typename ConcurrentMap::Iterator iter(m_map); TileType *tile = 0; while (iter.isValid()) { m_map.getGC().lockRawPointerAccess(); tile = m_map.erase(iter.getKey()); if (tile) { tile->notifyDetachedFromDataManager(); m_map.getGC().enqueue(&MemoryReclaimer::destroy, new MemoryReclaimer(tile)); } m_map.getGC().unlockRawPointerAccess(); iter.next(); } m_numTiles.store(0); } // garbage collection must **not** be run with locks held m_map.getGC().update(false); } template inline void KisTileHashTableTraits2::setDefaultTileData(KisTileData *defaultTileData) { QWriteLocker locker(&m_defaultPixelDataLock); if (m_defaultTileData) { m_defaultTileData->release(); m_defaultTileData = 0; } if (defaultTileData) { defaultTileData->acquire(); m_defaultTileData = defaultTileData; } } template inline KisTileData* KisTileHashTableTraits2::defaultTileData() { QReadLocker locker(&m_defaultPixelDataLock); return m_defaultTileData; } template void KisTileHashTableTraits2::debugPrintInfo() { } template void KisTileHashTableTraits2::debugMaxListLength(qint32 &/*min*/, qint32 &/*max*/) { } typedef KisTileHashTableTraits2 KisTileHashTable; typedef KisTileHashTableIteratorTraits2 KisTileHashTableIterator; typedef KisTileHashTableIteratorTraits2 KisTileHashTableConstIterator; #endif // KIS_TILEHASHTABLE_2_H diff --git a/libs/pigment/resources/KoColorSet.cpp b/libs/pigment/resources/KoColorSet.cpp index 2019d77c3b..f190c0d1c2 100644 --- a/libs/pigment/resources/KoColorSet.cpp +++ b/libs/pigment/resources/KoColorSet.cpp @@ -1,1651 +1,1651 @@ /* This file is part of the KDE project Copyright (c) 2005 Boudewijn Rempt Copyright (c) 2016 L. E. Segovia This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // qFromLittleEndian #include #include #include #include #include #include #include #include #include "KisSwatch.h" #include "KoColorSet.h" #include "KoColorSet_p.h" namespace { /** * readAllLinesSafe() reads all the lines in the byte array * using the automated UTF8 and CR/LF transformations. That * might be necessary for opening GPL palettes created on Linux * in Windows environment. */ QStringList readAllLinesSafe(QByteArray *data) { QStringList lines; QBuffer buffer(data); buffer.open(QBuffer::ReadOnly); QTextStream stream(&buffer); QString line; while (stream.readLineInto(&line)) { lines << line; } return lines; } } const QString KoColorSet::GLOBAL_GROUP_NAME = QString(); const QString KoColorSet::KPL_VERSION_ATTR = "version"; const QString KoColorSet::KPL_GROUP_ROW_COUNT_ATTR = "rows"; const QString KoColorSet::KPL_PALETTE_COLUMN_COUNT_ATTR = "columns"; const QString KoColorSet::KPL_PALETTE_NAME_ATTR = "name"; const QString KoColorSet::KPL_PALETTE_COMMENT_ATTR = "comment"; const QString KoColorSet::KPL_PALETTE_FILENAME_ATTR = "filename"; const QString KoColorSet::KPL_PALETTE_READONLY_ATTR = "readonly"; const QString KoColorSet::KPL_COLOR_MODEL_ID_ATTR = "colorModelId"; const QString KoColorSet::KPL_COLOR_DEPTH_ID_ATTR = "colorDepthId"; const QString KoColorSet::KPL_GROUP_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ROW_ATTR = "row"; const QString KoColorSet::KPL_SWATCH_COL_ATTR = "column"; const QString KoColorSet::KPL_SWATCH_NAME_ATTR = "name"; const QString KoColorSet::KPL_SWATCH_ID_ATTR = "id"; const QString KoColorSet::KPL_SWATCH_SPOT_ATTR = "spot"; const QString KoColorSet::KPL_SWATCH_BITDEPTH_ATTR = "bitdepth"; const QString KoColorSet::KPL_PALETTE_PROFILE_TAG = "Profile"; const QString KoColorSet::KPL_SWATCH_POS_TAG = "Position"; const QString KoColorSet::KPL_SWATCH_TAG = "ColorSetEntry"; const QString KoColorSet::KPL_GROUP_TAG = "Group"; const QString KoColorSet::KPL_PALETTE_TAG = "ColorSet"; const int MAXIMUM_ALLOWED_COLUMNS = 4096; KoColorSet::KoColorSet(const QString& filename) : KoResource(filename) , d(new Private(this)) { if (!filename.isEmpty()) { QFileInfo f(filename); setIsEditable(f.isWritable()); } } /// Create an copied palette KoColorSet::KoColorSet(const KoColorSet& rhs) : KoResource(rhs) , d(new Private(this)) { d->paletteType = rhs.d->paletteType; d->data = rhs.d->data; d->comment = rhs.d->comment; d->groupNames = rhs.d->groupNames; d->groups = rhs.d->groups; d->isGlobal = rhs.d->isGlobal; d->isEditable = rhs.d->isEditable; } KoColorSet::~KoColorSet() { } bool KoColorSet::load() { QFile file(filename()); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnPigment << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); if (!QFileInfo(filename()).isWritable()) { setIsEditable(false); } return res; } bool KoColorSet::loadFromDevice(QIODevice *dev) { if (!dev->isOpen()) dev->open(QIODevice::ReadOnly); d->data = dev->readAll(); Q_ASSERT(d->data.size() != 0); return d->init(); } bool KoColorSet::save() { if (d->isGlobal) { // save to resource dir QFile file(filename()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { return false; } saveToDevice(&file); file.close(); return true; } else { return true; // palette is not global, but still indicate that it's saved } } bool KoColorSet::saveToDevice(QIODevice *dev) const { bool res; switch(d->paletteType) { case GPL: res = d->saveGpl(dev); break; default: res = d->saveKpl(dev); } if (res) { KoResource::saveToDevice(dev); } return res; } QByteArray KoColorSet::toByteArray() const { QBuffer s; s.open(QIODevice::WriteOnly); if (!saveToDevice(&s)) { warnPigment << "saving palette failed:" << name(); return QByteArray(); } s.close(); s.open(QIODevice::ReadOnly); QByteArray res = s.readAll(); s.close(); return res; } bool KoColorSet::fromByteArray(QByteArray &data) { QBuffer buf(&data); buf.open(QIODevice::ReadOnly); return loadFromDevice(&buf); } KoColorSet::PaletteType KoColorSet::paletteType() const { return d->paletteType; } void KoColorSet::setPaletteType(PaletteType paletteType) { d->paletteType = paletteType; QString suffix; switch(d->paletteType) { case GPL: suffix = ".gpl"; break; case ACT: suffix = ".act"; break; case RIFF_PAL: case PSP_PAL: suffix = ".pal"; break; case ACO: suffix = ".aco"; break; case XML: suffix = ".xml"; break; case KPL: suffix = ".kpl"; break; case SBZ: suffix = ".sbz"; break; default: suffix = defaultFileExtension(); } QStringList fileName = filename().split("."); fileName.last() = suffix.replace(".", ""); setFilename(fileName.join(".")); } quint32 KoColorSet::colorCount() const { - int colorCount = d->groups[GLOBAL_GROUP_NAME].colorCount(); + int colorCount = 0; for (KisSwatchGroup &g : d->groups.values()) { colorCount += g.colorCount(); } return colorCount; } void KoColorSet::add(const KisSwatch &c, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.addEntry(c); } void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName) { KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) ? d->groups[groupName] : d->global(); modifiedGroup.setEntry(e, x, y); } void KoColorSet::clear() { d->groups.clear(); d->groupNames.clear(); d->groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); d->groupNames.append(GLOBAL_GROUP_NAME); } KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const { int yInGroup = y; QString nameGroupFoundIn; for (const QString &groupName : d->groupNames) { if (yInGroup < d->groups[groupName].rowCount()) { nameGroupFoundIn = groupName; break; } else { yInGroup -= d->groups[groupName].rowCount(); } } const KisSwatchGroup &groupFoundIn = nameGroupFoundIn == GLOBAL_GROUP_NAME ? d->global() : d->groups[nameGroupFoundIn]; Q_ASSERT(groupFoundIn.checkEntry(x, yInGroup)); return groupFoundIn.getEntry(x, yInGroup); } KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName) { KisSwatch e; const KisSwatchGroup &sourceGroup = groupName == QString() ? d->global() : d->groups[groupName]; if (sourceGroup.checkEntry(x, y)) { e = sourceGroup.getEntry(x, y); } return e; } QStringList KoColorSet::getGroupNames() { if (d->groupNames.size() != d->groups.size()) { warnPigment << "mismatch between groups and the groupnames list."; return QStringList(d->groups.keys()); } return d->groupNames; } bool KoColorSet::changeGroupName(const QString &oldGroupName, const QString &newGroupName) { if (!d->groups.contains(oldGroupName)) { return false; } if (oldGroupName == newGroupName) { return true; } d->groups[newGroupName] = d->groups[oldGroupName]; d->groups.remove(oldGroupName); d->groups[newGroupName].setName(newGroupName); //rename the string in the stringlist; int index = d->groupNames.indexOf(oldGroupName); d->groupNames.replace(index, newGroupName); return true; } void KoColorSet::setColumnCount(int columns) { d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns); for (KisSwatchGroup &g : d->groups.values()) { g.setColumnCount(columns); } } int KoColorSet::columnCount() const { return d->groups[GLOBAL_GROUP_NAME].columnCount(); } QString KoColorSet::comment() { return d->comment; } void KoColorSet::setComment(QString comment) { d->comment = comment; } bool KoColorSet::addGroup(const QString &groupName) { if (d->groups.contains(groupName) || d->groupNames.contains(groupName)) { return false; } d->groupNames.append(groupName); d->groups[groupName] = KisSwatchGroup(); d->groups[groupName].setName(groupName); return true; } bool KoColorSet::moveGroup(const QString &groupName, const QString &groupNameInsertBefore) { if (d->groupNames.contains(groupName)==false || d->groupNames.contains(groupNameInsertBefore)==false) { return false; } if (groupNameInsertBefore != GLOBAL_GROUP_NAME && groupName != GLOBAL_GROUP_NAME) { d->groupNames.removeAt(d->groupNames.indexOf(groupName)); int index = d->groupNames.indexOf(groupNameInsertBefore); d->groupNames.insert(index, groupName); } return true; } bool KoColorSet::removeGroup(const QString &groupName, bool keepColors) { if (!d->groups.contains(groupName)) { return false; } if (groupName == GLOBAL_GROUP_NAME) { return false; } if (keepColors) { // put all colors directly below global int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount(); for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) { d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch, info.column, info.row + startingRow); } } d->groupNames.removeAt(d->groupNames.indexOf(groupName)); d->groups.remove(groupName); return true; } QString KoColorSet::defaultFileExtension() const { return QString(".kpl"); } int KoColorSet::rowCount() const { int res = 0; for (const QString &name : d->groupNames) { res += d->groups[name].rowCount(); } return res; } KisSwatchGroup *KoColorSet::getGroup(const QString &name) { if (!d->groups.contains(name)) { return 0; } return &(d->groups[name]); } KisSwatchGroup *KoColorSet::getGlobalGroup() { return getGroup(GLOBAL_GROUP_NAME); } bool KoColorSet::isGlobal() const { return d->isGlobal; } void KoColorSet::setIsGlobal(bool isGlobal) { d->isGlobal = isGlobal; } bool KoColorSet::isEditable() const { return d->isEditable; } void KoColorSet::setIsEditable(bool isEditable) { d->isEditable = isEditable; } KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) { KisSwatchGroup::SwatchInfo res; quint8 highestPercentage = 0; quint8 testPercentage = 0; for (const QString &groupName : getGroupNames()) { KisSwatchGroup *group = getGroup(groupName); for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) { KoColor color = currInfo.swatch.color(); if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) { color.convertTo(compare.colorSpace()); } else if (compare.colorSpace() != color.colorSpace()) { compare.convertTo(color.colorSpace()); } testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data())); if (testPercentage > highestPercentage) { highestPercentage = testPercentage; res = currInfo; } } } return res; } /********************************KoColorSet::Private**************************/ KoColorSet::Private::Private(KoColorSet *a_colorSet) : colorSet(a_colorSet) , isGlobal(true) , isEditable(false) { groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); } KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba) { QFileInfo fi(fileName); // .pal if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { return KoColorSet::RIFF_PAL; } // .gpl else if (ba.startsWith("GIMP Palette")) { return KoColorSet::GPL; } // .pal else if (ba.startsWith("JASC-PAL")) { return KoColorSet::PSP_PAL; } else if (fi.suffix().toLower() == "aco") { return KoColorSet::ACO; } else if (fi.suffix().toLower() == "act") { return KoColorSet::ACT; } else if (fi.suffix().toLower() == "xml") { return KoColorSet::XML; } else if (fi.suffix().toLower() == "kpl") { return KoColorSet::KPL; } else if (fi.suffix().toLower() == "sbz") { return KoColorSet::SBZ; } return KoColorSet::UNKNOWN; } void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) { KisSwatch colorEntry; // It's a color, retrieve it QXmlStreamAttributes colorProperties = xml->attributes(); QStringRef colorName = colorProperties.value("NAME"); colorEntry.setName(colorName.isEmpty() || colorName.isNull() ? i18n("Untitled") : colorName.toString()); // RGB or CMYK? if (colorProperties.hasAttribute("RGB")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", RGB " << colorProperties.value("RGB"); KoColor currentColor(KoColorSpaceRegistry::instance()->rgb8()); QStringRef colorValue = colorProperties.value("RGB"); if (colorValue.length() != 7 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid rgb8 color (malformed): " + colorValue); return; } else { bool rgbOk; quint32 rgb = colorValue.mid(1).toUInt(&rgbOk, 16); if (!rgbOk) { xml->raiseError("Invalid rgb8 color (unable to convert): " + colorValue); return; } quint8 r = rgb >> 16 & 0xff; quint8 g = rgb >> 8 & 0xff; quint8 b = rgb & 0xff; dbgPigment << "Color parsed: "<< r << g << b; currentColor.data()[0] = r; currentColor.data()[1] = g; currentColor.data()[2] = b; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else if (colorProperties.hasAttribute("CMYK")) { dbgPigment << "Color " << colorProperties.value("NAME") << ", CMYK " << colorProperties.value("CMYK"); KoColor currentColor(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer8BitsColorDepthID.id(), QString())); QStringRef colorValue = colorProperties.value("CMYK"); if (colorValue.length() != 9 && colorValue.at(0) != '#') { // Color is a hexadecimal number xml->raiseError("Invalid cmyk color (malformed): " % colorValue); return; } else { bool cmykOk; quint32 cmyk = colorValue.mid(1).toUInt(&cmykOk, 16); // cmyk uses the full 32 bits if (!cmykOk) { xml->raiseError("Invalid cmyk color (unable to convert): " % colorValue); return; } quint8 c = cmyk >> 24 & 0xff; quint8 m = cmyk >> 16 & 0xff; quint8 y = cmyk >> 8 & 0xff; quint8 k = cmyk & 0xff; dbgPigment << "Color parsed: "<< c << m << y << k; currentColor.data()[0] = c; currentColor.data()[1] = m; currentColor.data()[2] = y; currentColor.data()[3] = k; currentColor.setOpacity(OPACITY_OPAQUE_U8); colorEntry.setColor(currentColor); set->add(colorEntry); while(xml->readNextStartElement()) { //ignore - these are all unknown or the /> element tag xml->skipCurrentElement(); } return; } } else { xml->raiseError("Unknown color space for color " + colorEntry.name()); } } bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) { //1. Get name QXmlStreamAttributes paletteProperties = xml->attributes(); QStringRef paletteName = paletteProperties.value("Name"); dbgPigment << "Processed name of palette:" << paletteName; set->setName(paletteName.toString()); //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them while(xml->readNextStartElement()) { QStringRef currentElement = xml->name(); if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { scribusParseColor(set, xml); } else { xml->skipCurrentElement(); } } if(xml->hasError()) { return false; } return true; } quint16 KoColorSet::Private::readShort(QIODevice *io) { quint16 val; quint64 read = io->read((char*)&val, 2); if (read != 2) return false; return qFromBigEndian(val); } bool KoColorSet::Private::init() { // just in case this is a reload (eg by KoEditColorSetDialog), groupNames.clear(); groups.clear(); groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); if (colorSet->filename().isNull()) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set"; return false; } if (data.isNull()) { QFile file(colorSet->filename()); if (file.size() == 0) { warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available"; return false; } file.open(QIODevice::ReadOnly); data = file.readAll(); file.close(); } bool res = false; paletteType = detectFormat(colorSet->filename(), data); switch(paletteType) { case GPL: res = loadGpl(); break; case ACT: res = loadAct(); break; case RIFF_PAL: res = loadRiff(); break; case PSP_PAL: res = loadPsp(); break; case ACO: res = loadAco(); break; case XML: res = loadXml(); break; case KPL: res = loadKpl(); break; case SBZ: res = loadSbz(); break; default: res = false; } colorSet->setValid(res); QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32); QPainter gc(&img); gc.fillRect(img.rect(), Qt::darkGray); for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) { QColor c = info.swatch.color().toQColor(); gc.fillRect(info.column * 4, info.row * 4, 4, 4, c); } colorSet->setImage(img); colorSet->setValid(res); data.clear(); return res; } bool KoColorSet::Private::saveGpl(QIODevice *dev) const { Q_ASSERT(dev->isOpen()); Q_ASSERT(dev->isWritable()); QTextStream stream(dev); stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n"; /* * Qt doesn't provide an interface to get a const reference to a QHash, that is * the underlying data structure of groups. Therefore, directly use * groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const */ for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) { for (int x = 0; x < colorSet->columnCount(); x++) { if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) { continue; } const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y); QColor c = entry.color().toQColor(); stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; if (entry.name().isEmpty()) stream << "Untitled\n"; else stream << entry.name() << "\n"; } } return true; } bool KoColorSet::Private::loadGpl() { if (data.isEmpty() || data.isNull() || data.length() < 50) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } quint32 index = 0; QStringList lines = readAllLinesSafe(&data); if (lines.size() < 3) { warnPigment << "Not enough lines in palette file: " << colorSet->filename(); return false; } QString columnsText; qint32 r, g, b; KisSwatch e; // Read name if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); return false; } colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); index = 2; // Read columns int columns = 0; if (lines[index].toLower().contains("columns")) { columnsText = lines[index].split(":")[1].trimmed(); columns = columnsText.toInt(); if (columns > MAXIMUM_ALLOWED_COLUMNS) { warnPigment << "Refusing to set unreasonable number of columns (" << columns << ") in GIMP Palette file " << colorSet->filename() << " - using maximum number of allowed columns instead"; global().setColumnCount(MAXIMUM_ALLOWED_COLUMNS); } else { global().setColumnCount(columns); } index = 3; } for (qint32 i = index; i < lines.size(); i++) { if (lines[i].startsWith('#')) { comment += lines[i].mid(1).trimmed() + ' '; } else if (!lines[i].isEmpty()) { QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() < 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); for (int i = 0; i != 3; i++) { a.pop_front(); } QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); global().addEntry(e); } } int rowCount = global().colorCount()/ global().columnCount(); if (global().colorCount() % global().columnCount()>0) { rowCount ++; } global().setRowCount(rowCount); return true; } bool KoColorSet::Private::loadAct() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; for (int i = 0; i < data.size(); i += 3) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); global().addEntry(e); } return true; } bool KoColorSet::Private::loadRiff() { // http://worms2d.info/Palette_file QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; RiffHeader header; memcpy(&header, data.constData(), sizeof(RiffHeader)); header.colorcount = qFromBigEndian(header.colorcount); for (int i = sizeof(RiffHeader); (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size()); i += 4) { quint8 r = data[i]; quint8 g = data[i+1]; quint8 b = data[i+2]; e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadPsp() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); KisSwatch e; qint32 r, g, b; QStringList l = readAllLinesSafe(&data); if (l.size() < 4) return false; if (l[0] != "JASC-PAL") return false; if (l[1] != "0100") return false; int entries = l[2].toInt(); for (int i = 0; i < entries; ++i) { QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); if (a.count() != 3) { continue; } r = qBound(0, a[0].toInt(), 255); g = qBound(0, a[1].toInt(), 255); b = qBound(0, a[2].toInt(), 255); e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); QString name = a.join(" "); e.setName(name.isEmpty() ? i18n("Untitled") : name); groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } return true; } bool KoColorSet::Private::loadKpl() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) { return false; } if (store->hasFile("profiles.xml")) { if (!store->open("profiles.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); QDomElement c = e.firstChildElement(KPL_PALETTE_PROFILE_TAG); while (!c.isNull()) { QString name = c.attribute(KPL_PALETTE_NAME_ATTR); QString filename = c.attribute(KPL_PALETTE_FILENAME_ATTR); QString colorModelId = c.attribute(KPL_COLOR_MODEL_ID_ATTR); QString colorDepthId = c.attribute(KPL_COLOR_DEPTH_ID_ATTR); if (!KoColorSpaceRegistry::instance()->profileByName(name)) { store->open(filename); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(colorModelId, colorDepthId, data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); } } c = c.nextSiblingElement(); } } { if (!store->open("colorset.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); int desiredColumnCount; QDomDocument doc; doc.setContent(ba); QDomElement e = doc.documentElement(); colorSet->setName(e.attribute(KPL_PALETTE_NAME_ATTR)); colorSet->setIsEditable(e.attribute(KPL_PALETTE_READONLY_ATTR) != "true"); comment = e.attribute(KPL_PALETTE_COMMENT_ATTR); desiredColumnCount = e.attribute(KPL_PALETTE_COLUMN_COUNT_ATTR).toInt(); if (desiredColumnCount > MAXIMUM_ALLOWED_COLUMNS) { warnPigment << "Refusing to set unreasonable number of columns (" << desiredColumnCount << ") in KPL palette file " << colorSet->filename() << " - setting maximum allowed column count instead."; colorSet->setColumnCount(MAXIMUM_ALLOWED_COLUMNS); } else { colorSet->setColumnCount(desiredColumnCount); } loadKplGroup(doc, e, colorSet->getGlobalGroup()); QDomElement g = e.firstChildElement(KPL_GROUP_TAG); while (!g.isNull()) { QString groupName = g.attribute(KPL_GROUP_NAME_ATTR); colorSet->addGroup(groupName); loadKplGroup(doc, g, colorSet->getGroup(groupName)); g = g.nextSiblingElement(KPL_GROUP_TAG); } } buf.close(); return true; } bool KoColorSet::Private::loadAco() { QFileInfo info(colorSet->filename()); colorSet->setName(info.baseName()); QBuffer buf(&data); buf.open(QBuffer::ReadOnly); quint16 version = readShort(&buf); quint16 numColors = readShort(&buf); KisSwatch e; if (version == 1 && buf.size() > 4+numColors*10) { buf.seek(4+numColors*10); version = readShort(&buf); numColors = readShort(&buf); } const quint16 quint16_MAX = 65535; for (int i = 0; i < numColors && !buf.atEnd(); ++i) { quint16 colorSpace = readShort(&buf); quint16 ch1 = readShort(&buf); quint16 ch2 = readShort(&buf); quint16 ch3 = readShort(&buf); quint16 ch4 = readShort(&buf); bool skip = false; if (colorSpace == 0) { // RGB const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); KoColor c(KoColorSpaceRegistry::instance()->rgb16(srgb)); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 1) { // HSB QColor qc; qc.setHsvF(ch1 / 65536.0, ch2 / 65536.0, ch3 / 65536.0); KoColor c(qc, KoColorSpaceRegistry::instance()->rgb16()); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 2) { // CMYK KoColor c(KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = quint16_MAX - ch1; reinterpret_cast(c.data())[1] = quint16_MAX - ch2; reinterpret_cast(c.data())[2] = quint16_MAX - ch3; reinterpret_cast(c.data())[3] = quint16_MAX - ch4; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 7) { // LAB KoColor c = KoColor(KoColorSpaceRegistry::instance()->lab16()); reinterpret_cast(c.data())[0] = ch3; reinterpret_cast(c.data())[1] = ch2; reinterpret_cast(c.data())[2] = ch1; c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else if (colorSpace == 8) { // GRAY KoColor c(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = ch1 * (quint16_MAX / 10000); c.setOpacity(OPACITY_OPAQUE_U8); e.setColor(c); } else { warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")"; skip = true; } if (version == 2) { quint16 v2 = readShort(&buf); //this isn't a version, it's a marker and needs to be skipped. Q_UNUSED(v2); quint16 size = readShort(&buf) -1; //then comes the length if (size>0) { QByteArray ba = buf.read(size*2); if (ba.size() == size*2) { QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); e.setName(Utf16Codec->toUnicode(ba)); } else { warnPigment << "Version 2 name block is the wrong size" << colorSet->filename(); } } v2 = readShort(&buf); //end marker also needs to be skipped. Q_UNUSED(v2); } if (!skip) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); } } return true; } bool KoColorSet::Private::loadSbz() { QBuffer buf(&data); buf.open(QBuffer::ReadOnly); // &buf is a subclass of QIODevice QScopedPointer store(KoStore::createStore(&buf, KoStore::Read, "application/x-swatchbook", KoStore::Zip)); if (!store || store->bad()) return false; if (store->hasFile("swatchbook.xml")) { // Try opening... if (!store->open("swatchbook.xml")) { return false; } QByteArray data; data.resize(store->size()); QByteArray ba = store->read(store->size()); store->close(); dbgPigment << "XML palette: " << colorSet->filename() << ", SwatchBooker format"; QDomDocument doc; int errorLine, errorColumn; QString errorMessage; bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn); if (!status) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; return false; } QDomElement e = doc.documentElement(); // SwatchBook // Start reading properties... QDomElement metadata = e.firstChildElement("metadata"); if (e.isNull()) { warnPigment << "Palette metadata not found"; return false; } QDomElement title = metadata.firstChildElement("dc:title"); QString colorName = title.text(); colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; colorSet->setName(colorName); dbgPigment << "Processed name of palette:" << colorSet->name(); // End reading properties // Now read colors... QDomElement materials = e.firstChildElement("materials"); if (materials.isNull()) { warnPigment << "Materials (color definitions) not found"; return false; } // This one has lots of "color" elements QDomElement colorElement = materials.firstChildElement("color"); if (colorElement.isNull()) { warnPigment << "Color definitions not found (line" << materials.lineNumber() << ", column" << materials.columnNumber() << ")"; return false; } // Also read the swatch book... QDomElement book = e.firstChildElement("book"); if (book.isNull()) { warnPigment << "Palette book (swatch composition) not found (line" << e.lineNumber() << ", column" << e.columnNumber() << ")"; return false; } // Which has lots of "swatch"es (todo: support groups) QDomElement swatch = book.firstChildElement(); if (swatch.isNull()) { warnPigment << "Swatches/groups definition not found (line" << book.lineNumber() << ", column" << book.columnNumber() << ")"; return false; } // We'll store colors here, and as we process swatches // we'll add them to the palette QHash materialsBook; QHash fileColorSpaces; // Color processing for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) { KisSwatch currentEntry; // Set if color is spot currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); // inside contains id and name // one or more define the color QDomElement currentColorMetadata = colorElement.firstChildElement("metadata"); QDomNodeList currentColorValues = colorElement.elementsByTagName("values"); // Get color name QDomElement colorTitle = currentColorMetadata.firstChildElement("dc:title"); QDomElement colorId = currentColorMetadata.firstChildElement("dc:identifier"); // Is there an id? (we need that at the very least for identifying a color) if (colorId.text().isEmpty()) { warnPigment << "Unidentified color (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } if (materialsBook.contains(colorId.text())) { warnPigment << "Duplicated color definition (line" << colorId.lineNumber()<< ", column" << colorId.columnNumber() << ")"; return false; } // Get a valid color name currentEntry.setId(colorId.text()); currentEntry.setName(colorTitle.text().isEmpty() ? colorId.text() : colorTitle.text()); // Get a valid color definition if (currentColorValues.isEmpty()) { warnPigment << "Color definitions not found (line" << colorElement.lineNumber() << ", column" << colorElement.columnNumber() << ")"; return false; } bool firstDefinition = false; const KoColorProfile *srgb = KoColorSpaceRegistry::instance()->rgb8()->profile(); // Priority: Lab, otherwise the first definition found for(int j = 0; j < currentColorValues.size(); j++) { QDomNode colorValue = currentColorValues.at(j); QDomElement colorValueE = colorValue.toElement(); QString model = colorValueE.attribute("model", QString()); // sRGB,RGB,HSV,HSL,CMY,CMYK,nCLR: 0 -> 1 // YIQ: Y 0 -> 1 : IQ -0.5 -> 0.5 // Lab: L 0 -> 100 : ab -128 -> 127 // XYZ: 0 -> ~100 if (model == "Lab") { QStringList lab = colorValueE.text().split(" "); if (lab.length() != 3) { warnPigment << "Invalid Lab color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float l = lab.at(0).toFloat(&status); float a = lab.at(1).toFloat(&status); float b = lab.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(LABAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = l; reinterpret_cast(c.data())[1] = a; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); firstDefinition = true; currentEntry.setColor(c); break; // Immediately add this one } else if (model == "sRGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid sRGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb)); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "XYZ" && !firstDefinition) { QStringList xyz = colorValueE.text().split(" "); if (xyz.length() != 3) { warnPigment << "Invalid XYZ color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float x = xyz.at(0).toFloat(&status); float y = xyz.at(1).toFloat(&status); float z = xyz.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } KoColor c(KoColorSpaceRegistry::instance()->colorSpace(XYZAColorModelID.id(), Float32BitsColorDepthID.id(), QString())); reinterpret_cast(c.data())[0] = x; reinterpret_cast(c.data())[1] = y; reinterpret_cast(c.data())[2] = z; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } // The following color spaces admit an ICC profile (in SwatchBooker) else if (model == "CMYK" && !firstDefinition) { QStringList cmyk = colorValueE.text().split(" "); if (cmyk.length() != 4) { warnPigment << "Invalid CMYK color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float c = cmyk.at(0).toFloat(&status); float m = cmyk.at(1).toFloat(&status); float y = cmyk.at(2).toFloat(&status); float k = cmyk.at(3).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor color(colorSpace); reinterpret_cast(color.data())[0] = c; reinterpret_cast(color.data())[1] = m; reinterpret_cast(color.data())[2] = y; reinterpret_cast(color.data())[3] = k; color.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(color); firstDefinition = true; } else if (model == "GRAY" && !firstDefinition) { QString gray = colorValueE.text(); float g = gray.toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float32BitsColorDepthID.id(), QString()); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(CMYKAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = g; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else if (model == "RGB" && !firstDefinition) { QStringList rgb = colorValueE.text().split(" "); if (rgb.length() != 3) { warnPigment << "Invalid RGB color definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } float r = rgb.at(0).toFloat(&status); float g = rgb.at(1).toFloat(&status); float b = rgb.at(2).toFloat(&status); if (!status) { warnPigment << "Invalid float definition (line" << colorValueE.lineNumber() << ", column" << colorValueE.columnNumber() << ")"; } QString space = colorValueE.attribute("space"); const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), srgb); if (!space.isEmpty()) { // Try loading the profile and add it to the registry if (!fileColorSpaces.contains(space)) { store->enterDirectory("profiles"); store->open(space); QByteArray data; data.resize(store->size()); data = store->read(store->size()); store->close(); const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), data); if (profile && profile->valid()) { KoColorSpaceRegistry::instance()->addProfile(profile); colorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), profile); fileColorSpaces.insert(space, colorSpace); } } else { colorSpace = fileColorSpaces.value(space); } } KoColor c(colorSpace); reinterpret_cast(c.data())[0] = r; reinterpret_cast(c.data())[1] = g; reinterpret_cast(c.data())[2] = b; c.setOpacity(OPACITY_OPAQUE_F); currentEntry.setColor(c); firstDefinition = true; } else { warnPigment << "Color space not implemented:" << model << "(line" << colorValueE.lineNumber() << ", column "<< colorValueE.columnNumber() << ")"; } } if (firstDefinition) { materialsBook.insert(currentEntry.id(), currentEntry); } else { warnPigment << "No supported color spaces for the current color (line" << colorElement.lineNumber() << ", column "<< colorElement.columnNumber() << ")"; return false; } } // End colors // Now decide which ones will go into the palette for(;!swatch.isNull(); swatch = swatch.nextSiblingElement()) { QString type = swatch.tagName(); if (type.isEmpty() || type.isNull()) { warnPigment << "Invalid swatch/group definition (no id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } else if (type == "swatch") { QString id = swatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; return false; } } else if (type == "group") { QDomElement groupMetadata = swatch.firstChildElement("metadata"); if (groupMetadata.isNull()) { warnPigment << "Invalid group definition (missing metadata) (line" << groupMetadata.lineNumber() << ", column" << groupMetadata.columnNumber() << ")"; return false; } QDomElement groupTitle = metadata.firstChildElement("dc:title"); if (groupTitle.isNull()) { warnPigment << "Invalid group definition (missing title) (line" << groupTitle.lineNumber() << ", column" << groupTitle.columnNumber() << ")"; return false; } QString currentGroupName = groupTitle.text(); QDomElement groupSwatch = swatch.firstChildElement("swatch"); while(!groupSwatch.isNull()) { QString id = groupSwatch.attribute("material"); if (id.isEmpty() || id.isNull()) { warnPigment << "Invalid swatch definition (no material id) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } if (materialsBook.contains(id)) { groups[currentGroupName].addEntry(materialsBook.value(id)); } else { warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; return false; } groupSwatch = groupSwatch.nextSiblingElement("swatch"); } } } // End palette } buf.close(); return true; } bool KoColorSet::Private::loadXml() { bool res = false; QXmlStreamReader *xml = new QXmlStreamReader(data); if (xml->readNextStartElement()) { QStringRef paletteId = xml->name(); if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus dbgPigment << "XML palette: " << colorSet->filename() << ", Scribus format"; res = loadScribusXmlPalette(colorSet, xml); } else { // Unknown XML format xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); } } // If there is any error (it should be returned through the stream) if (xml->hasError() || !res) { warnPigment << "Illegal XML palette:" << colorSet->filename(); warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); return false; } else { dbgPigment << "XML palette parsed successfully:" << colorSet->filename(); return true; } } bool KoColorSet::Private::saveKpl(QIODevice *dev) const { QScopedPointer store(KoStore::createStore(dev, KoStore::Write, "krita/x-colorset", KoStore::Zip)); if (!store || store->bad()) return false; QSet colorSpaces; { QDomDocument doc; QDomElement root = doc.createElement(KPL_PALETTE_TAG); root.setAttribute(KPL_VERSION_ATTR, "1.0"); root.setAttribute(KPL_PALETTE_NAME_ATTR, colorSet->name()); root.setAttribute(KPL_PALETTE_COMMENT_ATTR, comment); root.setAttribute(KPL_PALETTE_READONLY_ATTR, (colorSet->isEditable() || !colorSet->isGlobal()) ? "false" : "true"); root.setAttribute(KPL_PALETTE_COLUMN_COUNT_ATTR, colorSet->columnCount()); root.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount()); saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces); for (const QString &groupName : groupNames) { if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; } QDomElement gl = doc.createElement(KPL_GROUP_TAG); gl.setAttribute(KPL_GROUP_NAME_ATTR, groupName); root.appendChild(gl); saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces); } doc.appendChild(root); if (!store->open("colorset.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } } QDomDocument doc; QDomElement profileElement = doc.createElement("Profiles"); for (const KoColorSpace *colorSpace : colorSpaces) { QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName(); if (!store->open(fn)) { return false; } QByteArray profileRawData = colorSpace->profile()->rawData(); if (!store->write(profileRawData)) { return false; } if (!store->close()) { return false; } QDomElement el = doc.createElement(KPL_PALETTE_PROFILE_TAG); el.setAttribute(KPL_PALETTE_FILENAME_ATTR, fn); el.setAttribute(KPL_PALETTE_NAME_ATTR, colorSpace->profile()->name()); el.setAttribute(KPL_COLOR_MODEL_ID_ATTR, colorSpace->colorModelId().id()); el.setAttribute(KPL_COLOR_DEPTH_ID_ATTR, colorSpace->colorDepthId().id()); profileElement.appendChild(el); } doc.appendChild(profileElement); if (!store->open("profiles.xml")) { return false; } QByteArray ba = doc.toByteArray(); if (store->write(ba) != ba.size()) { return false; } if (!store->close()) { return false; } return store->finalize(); } void KoColorSet::Private::saveKplGroup(QDomDocument &doc, QDomElement &groupEle, const KisSwatchGroup *group, QSet &colorSetSet) const { groupEle.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, QString::number(group->rowCount())); for (const SwatchInfoType &info : group->infoList()) { const KoColorProfile *profile = info.swatch.color().colorSpace()->profile(); // Only save non-builtin profiles.= if (!profile->fileName().isEmpty()) { colorSetSet.insert(info.swatch.color().colorSpace()); } QDomElement swatchEle = doc.createElement(KPL_SWATCH_TAG); swatchEle.setAttribute(KPL_SWATCH_NAME_ATTR, info.swatch.name()); swatchEle.setAttribute(KPL_SWATCH_ID_ATTR, info.swatch.id()); swatchEle.setAttribute(KPL_SWATCH_SPOT_ATTR, info.swatch.spotColor() ? "true" : "false"); swatchEle.setAttribute(KPL_SWATCH_BITDEPTH_ATTR, info.swatch.color().colorSpace()->colorDepthId().id()); info.swatch.color().toXML(doc, swatchEle); QDomElement positionEle = doc.createElement(KPL_SWATCH_POS_TAG); positionEle.setAttribute(KPL_SWATCH_ROW_ATTR, info.row); positionEle.setAttribute(KPL_SWATCH_COL_ATTR, info.column); swatchEle.appendChild(positionEle); groupEle.appendChild(swatchEle); } } void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group) { Q_UNUSED(doc); if (!parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()) { group->setRowCount(parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).toInt()); } group->setColumnCount(colorSet->columnCount()); for (QDomElement swatchEle = parentEle.firstChildElement(KPL_SWATCH_TAG); !swatchEle.isNull(); swatchEle = swatchEle.nextSiblingElement(KPL_SWATCH_TAG)) { QString colorDepthId = swatchEle.attribute(KPL_SWATCH_BITDEPTH_ATTR, Integer8BitsColorDepthID.id()); KisSwatch entry; entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId)); entry.setName(swatchEle.attribute(KPL_SWATCH_NAME_ATTR)); entry.setId(swatchEle.attribute(KPL_SWATCH_ID_ATTR)); entry.setSpotColor(swatchEle.attribute(KPL_SWATCH_SPOT_ATTR, "false") == "true" ? true : false); QDomElement positionEle = swatchEle.firstChildElement(KPL_SWATCH_POS_TAG); if (!positionEle.isNull()) { int rowNumber = positionEle.attribute(KPL_SWATCH_ROW_ATTR).toInt(); int columnNumber = positionEle.attribute(KPL_SWATCH_COL_ATTR).toInt(); if (columnNumber < 0 || columnNumber >= colorSet->columnCount() || rowNumber < 0 ) { warnPigment << "Swatch" << entry.name() << "of palette" << colorSet->name() << "has invalid position."; continue; } group->setEntry(entry, columnNumber, rowNumber); } else { group->addEntry(entry); } } if (parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull() && group->colorCount() > 0 && group->columnCount() > 0 && (group->colorCount() / (group->columnCount()) + 1) < 20) { group->setRowCount((group->colorCount() / group->columnCount()) + 1); } } diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp index d022cdef3b..6d3a0b5b61 100644 --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -1,2128 +1,2129 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" // XXX: remove #include // XXX: remove #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_layer_utils.h" // Local #include "KisViewManager.h" #include "kis_clipboard.h" #include "widgets/kis_custom_image_widget.h" #include "canvas/kis_canvas2.h" #include "flake/kis_shape_controller.h" #include "kis_statusbar.h" #include "widgets/kis_progress_widget.h" #include "kis_canvas_resource_provider.h" #include "KisResourceServerProvider.h" #include "kis_node_manager.h" #include "KisPart.h" #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisView.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_image_barrier_lock_adapter.h" #include "KisReferenceImagesLayer.h" #include #include "kis_config_notifier.h" #include "kis_async_action_feedback.h" #include "KisCloneDocumentStroke.h" #include // Define the protocol used here for embedded documents' URL // This used to "store" but QUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make QUrl happy and for document children #define INTERNAL_PROTOCOL "intern" #define INTERNAL_PREFIX "intern:/" // Warning, keep it sync in koStore.cc #include using namespace std; namespace { constexpr int errorMessageTimeout = 5000; constexpr int successMessageTimeout = 1000; } /********************************************************** * * KisDocument * **********************************************************/ //static QString KisDocument::newObjectName() { static int s_docIFNumber = 0; QString name; name.setNum(s_docIFNumber++); name.prepend("document_"); return name; } class UndoStack : public KUndo2Stack { public: UndoStack(KisDocument *doc) : KUndo2Stack(doc), m_doc(doc) { } void setIndex(int idx) override { KisImageWSP image = this->image(); image->requestStrokeCancellation(); if(image->tryBarrierLock()) { KUndo2Stack::setIndex(idx); image->unlock(); } } void notifySetIndexChangedOneCommand() override { KisImageWSP image = this->image(); image->unlock(); /** * Some very weird commands may emit blocking signals to * the GUI (e.g. KisGuiContextCommand). Here is the best thing * we can do to avoid the deadlock */ while(!image->tryBarrierLock()) { QApplication::processEvents(); } } void undo() override { KisImageWSP image = this->image(); image->requestUndoDuringStroke(); if (image->tryUndoUnfinishedLod0Stroke() == UNDO_OK) { return; } if(image->tryBarrierLock()) { KUndo2Stack::undo(); image->unlock(); } } void redo() override { KisImageWSP image = this->image(); if(image->tryBarrierLock()) { KUndo2Stack::redo(); image->unlock(); } } private: KisImageWSP image() { KisImageWSP currentImage = m_doc->image(); Q_ASSERT(currentImage); return currentImage; } private: KisDocument *m_doc; }; class Q_DECL_HIDDEN KisDocument::Private { public: Private(KisDocument *q) : docInfo(new KoDocumentInfo(q)) // deleted by QObject , importExportManager(new KisImportExportManager(q)) // deleted manually , autoSaveTimer(new QTimer(q)) , undoStack(new UndoStack(q)) // deleted by QObject , m_bAutoDetectedMime(false) , modified(false) , readwrite(true) , firstMod(QDateTime::currentDateTime()) , lastMod(firstMod) , nserver(new KisNameServer(1)) , imageIdleWatcher(2000 /*ms*/) , globalAssistantsColor(KisConfig(true).defaultAssistantsColor()) , savingLock(&savingMutex) , batchMode(false) { if (QLocale().measurementSystem() == QLocale::ImperialSystem) { unit = KoUnit::Inch; } else { unit = KoUnit::Centimeter; } } Private(const Private &rhs, KisDocument *q) : docInfo(new KoDocumentInfo(*rhs.docInfo, q)) , importExportManager(new KisImportExportManager(q)) , autoSaveTimer(new QTimer(q)) , undoStack(new UndoStack(q)) , nserver(new KisNameServer(*rhs.nserver)) , preActivatedNode(0) // the node is from another hierarchy! , imageIdleWatcher(2000 /*ms*/) , savingLock(&savingMutex) { copyFromImpl(rhs, q, CONSTRUCT); } ~Private() { // Don't delete m_d->shapeController because it's in a QObject hierarchy. delete nserver; } KoDocumentInfo *docInfo = 0; KoUnit unit; KisImportExportManager *importExportManager = 0; // The filter-manager to use when loading/saving [for the options] QByteArray mimeType; // The actual mimetype of the document QByteArray outputMimeType; // The mimetype to use when saving QTimer *autoSaveTimer; QString lastErrorMessage; // see openFile() QString lastWarningMessage; int autoSaveDelay = 300; // in seconds, 0 to disable. bool modifiedAfterAutosave = false; bool isAutosaving = false; bool disregardAutosaveFailure = false; int autoSaveFailureCount = 0; KUndo2Stack *undoStack = 0; KisGuidesConfig guidesConfig; KisMirrorAxisConfig mirrorAxisConfig; bool m_bAutoDetectedMime = false; // whether the mimetype in the arguments was detected by the part itself QUrl m_url; // local url - the one displayed to the user. QString m_file; // Local file - the only one the part implementation should deal with. QMutex savingMutex; bool modified = false; bool readwrite = false; QDateTime firstMod; QDateTime lastMod; KisNameServer *nserver; KisImageSP image; KisImageSP savingImage; KisNodeWSP preActivatedNode; KisShapeController* shapeController = 0; KoShapeController* koShapeController = 0; KisIdleWatcher imageIdleWatcher; QScopedPointer imageIdleConnection; QList assistants; QColor globalAssistantsColor; KisSharedPtr referenceImagesLayer; QList paletteList; KisGridConfig gridConfig; StdLockableWrapper savingLock; bool modifiedWhileSaving = false; QScopedPointer backgroundSaveDocument; QPointer savingUpdater; QFuture childSavingFuture; KritaUtils::ExportFileJob backgroundSaveJob; bool isRecovered = false; bool batchMode { false }; void setImageAndInitIdleWatcher(KisImageSP _image) { image = _image; imageIdleWatcher.setTrackedImage(image); if (image) { imageIdleConnection.reset( new KisSignalAutoConnection( &imageIdleWatcher, SIGNAL(startedIdleMode()), image.data(), SLOT(explicitRegenerateLevelOfDetail()))); } } void copyFrom(const Private &rhs, KisDocument *q); void copyFromImpl(const Private &rhs, KisDocument *q, KisDocument::CopyPolicy policy); /// clones the palette list oldList /// the ownership of the returned KoColorSet * belongs to the caller QList clonePaletteList(const QList &oldList); class StrippedSafeSavingLocker; }; void KisDocument::Private::copyFrom(const Private &rhs, KisDocument *q) { copyFromImpl(rhs, q, KisDocument::REPLACE); } void KisDocument::Private::copyFromImpl(const Private &rhs, KisDocument *q, KisDocument::CopyPolicy policy) { if (policy == REPLACE) { delete docInfo; } docInfo = (new KoDocumentInfo(*rhs.docInfo, q)); unit = rhs.unit; mimeType = rhs.mimeType; outputMimeType = rhs.outputMimeType; if (policy == REPLACE) { q->setGuidesConfig(rhs.guidesConfig); q->setMirrorAxisConfig(rhs.mirrorAxisConfig); q->setModified(rhs.modified); q->setAssistants(KisPaintingAssistant::cloneAssistantList(rhs.assistants)); q->setGridConfig(rhs.gridConfig); } else { // in CONSTRUCT mode, we cannot use the functions of KisDocument // because KisDocument does not yet have a pointer to us. guidesConfig = rhs.guidesConfig; mirrorAxisConfig = rhs.mirrorAxisConfig; modified = rhs.modified; assistants = KisPaintingAssistant::cloneAssistantList(rhs.assistants); gridConfig = rhs.gridConfig; } m_bAutoDetectedMime = rhs.m_bAutoDetectedMime; m_url = rhs.m_url; m_file = rhs.m_file; readwrite = rhs.readwrite; firstMod = rhs.firstMod; lastMod = rhs.lastMod; // XXX: the display properties will be shared between different snapshots globalAssistantsColor = rhs.globalAssistantsColor; if (policy == REPLACE) { QList newPaletteList = clonePaletteList(rhs.paletteList); q->setPaletteList(newPaletteList, /* emitSignal = */ true); // we still do not own palettes if we did not } else { paletteList = rhs.paletteList; } batchMode = rhs.batchMode; } QList KisDocument::Private::clonePaletteList(const QList &oldList) { QList newList; Q_FOREACH (KoColorSetSP palette, oldList) { newList.append(QSharedPointer(new KoColorSet(*palette.data()))); } return newList; } class KisDocument::Private::StrippedSafeSavingLocker { public: StrippedSafeSavingLocker(QMutex *savingMutex, KisImageSP image) : m_locked(false) , m_image(image) , m_savingLock(savingMutex) , m_imageLock(image, true) { /** * Initial try to lock both objects. Locking the image guards * us from any image composition threads running in the * background, while savingMutex guards us from entering the * saving code twice by autosave and main threads. * * Since we are trying to lock multiple objects, so we should * do it in a safe manner. */ m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; if (!m_locked) { m_image->requestStrokeEnd(); QApplication::processEvents(); // one more try... m_locked = std::try_lock(m_imageLock, m_savingLock) < 0; } } ~StrippedSafeSavingLocker() { if (m_locked) { m_imageLock.unlock(); m_savingLock.unlock(); } } bool successfullyLocked() const { return m_locked; } private: bool m_locked; KisImageSP m_image; StdLockableWrapper m_savingLock; KisImageBarrierLockAdapter m_imageLock; }; KisDocument::KisDocument() : d(new Private(this)) { connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); connect(d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setObjectName(newObjectName()); // preload the krita resources KisResourceServerProvider::instance(); d->shapeController = new KisShapeController(this, d->nserver); d->koShapeController = new KoShapeController(0, d->shapeController); d->shapeController->resourceManager()->setGlobalShapeController(d->koShapeController); slotConfigChanged(); } KisDocument::KisDocument(const KisDocument &rhs) : QObject(), d(new Private(*rhs.d, this)) { copyFromDocumentImpl(rhs, CONSTRUCT); } KisDocument::~KisDocument() { // wait until all the pending operations are in progress waitForSavingToComplete(); /** * Push a timebomb, which will try to release the memory after * the document has been deleted */ KisPaintDevice::createMemoryReleaseObject()->deleteLater(); d->autoSaveTimer->disconnect(this); d->autoSaveTimer->stop(); delete d->importExportManager; // Despite being QObject they needs to be deleted before the image delete d->shapeController; delete d->koShapeController; if (d->image) { d->image->notifyAboutToBeDeleted(); /** * WARNING: We should wait for all the internal image jobs to * finish before entering KisImage's destructor. The problem is, * while execution of KisImage::~KisImage, all the weak shared * pointers pointing to the image enter an inconsistent * state(!). The shared counter is already zero and destruction * has started, but the weak reference doesn't know about it, * because KisShared::~KisShared hasn't been executed yet. So all * the threads running in background and having weak pointers will * enter the KisImage's destructor as well. */ d->image->requestStrokeCancellation(); d->image->waitForDone(); // clear undo commands that can still point to the image d->undoStack->clear(); d->image->waitForDone(); KisImageWSP sanityCheckPointer = d->image; Q_UNUSED(sanityCheckPointer); // The following line trigger the deletion of the image d->image.clear(); // check if the image has actually been deleted KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityCheckPointer.isValid()); } delete d; } bool KisDocument::reload() { // XXX: reimplement! return false; } KisDocument *KisDocument::clone() { return new KisDocument(*this); } bool KisDocument::exportDocumentImpl(const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) { QFileInfo filePathInfo(job.filePath); if (filePathInfo.exists() && !filePathInfo.isWritable()) { slotCompleteSavingDocument(job, ImportExportCodes::NoAccessToWrite, i18n("%1 cannot be written to. Please save under a different name.", job.filePath)); //return ImportExportCodes::NoAccessToWrite; return false; } KisConfig cfg(true); if (cfg.backupFile() && filePathInfo.exists()) { QString backupDir; switch(cfg.readEntry("backupfilelocation", 0)) { case 1: backupDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); break; case 2: backupDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation); break; default: // Do nothing: the empty string is user file location break; } int numOfBackupsKept = cfg.readEntry("numberofbackupfiles", 1); QString suffix = cfg.readEntry("backupfilesuffix", "~"); if (numOfBackupsKept == 1) { KBackup::simpleBackupFile(job.filePath, backupDir, suffix); } else if (numOfBackupsKept > 2) { KBackup::numberedBackupFile(job.filePath, backupDir, suffix, numOfBackupsKept); } } //KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false); if (job.mimeType.isEmpty()) { KisImportExportErrorCode error = ImportExportCodes::FileFormatIncorrect; slotCompleteSavingDocument(job, error, error.errorMessage()); return false; } const QString actionName = job.flags & KritaUtils::SaveIsExporting ? i18n("Exporting Document...") : i18n("Saving Document..."); bool started = initiateSavingInBackground(actionName, this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob, KisImportExportErrorCode ,QString)), job, exportConfiguration); if (!started) { emit canceled(QString()); } return started; } bool KisDocument::exportDocument(const QUrl &url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; SaveFlags flags = SaveIsExporting; if (showWarnings) { flags |= SaveShowWarnings; } KisUsageLogger::log(QString("Exporting Document: %1 as %2. %3 * %4 pixels, %5 layers, %6 frames, %7 framerate. Export configuration: %8") .arg(url.toLocalFile()) .arg(QString::fromLatin1(mimeType)) .arg(d->image->width()) .arg(d->image->height()) .arg(d->image->nlayers()) .arg(d->image->animationInterface()->totalLength()) .arg(d->image->animationInterface()->framerate()) .arg(exportConfiguration ? exportConfiguration->toXML() : "No configuration")); return exportDocumentImpl(KritaUtils::ExportFileJob(url.toLocalFile(), mimeType, flags), exportConfiguration); } bool KisDocument::saveAs(const QUrl &_url, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { using namespace KritaUtils; KisUsageLogger::log(QString("Saving Document %9 as %1 (mime: %2). %3 * %4 pixels, %5 layers. %6 frames, %7 framerate. Export configuration: %8") .arg(_url.toLocalFile()) .arg(QString::fromLatin1(mimeType)) .arg(d->image->width()) .arg(d->image->height()) .arg(d->image->nlayers()) .arg(d->image->animationInterface()->totalLength()) .arg(d->image->animationInterface()->framerate()) .arg(exportConfiguration ? exportConfiguration->toXML() : "No configuration") .arg(url().toLocalFile())); return exportDocumentImpl(ExportFileJob(_url.toLocalFile(), mimeType, showWarnings ? SaveShowWarnings : SaveNone), exportConfiguration); } bool KisDocument::save(bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { return saveAs(url(), mimeType(), showWarnings, exportConfiguration); } QByteArray KisDocument::serializeToNativeByteArray() { QByteArray byteArray; QBuffer buffer(&byteArray); QScopedPointer filter(KisImportExportManager::filterForMimeType(nativeFormatMimeType(), KisImportExportManager::Export)); filter->setBatchMode(true); filter->setMimeType(nativeFormatMimeType()); Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return byteArray; } d->savingImage = d->image; if (!filter->convert(this, &buffer).isOk()) { qWarning() << "serializeToByteArray():: Could not export to our native format"; } return byteArray; } void KisDocument::slotCompleteSavingDocument(const KritaUtils::ExportFileJob &job, KisImportExportErrorCode status, const QString &errorMessage) { if (status.isCancelled()) return; const QString fileName = QFileInfo(job.filePath).fileName(); if (!status.isOk()) { emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error message", "Error during saving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); if (!fileBatchMode()) { const QString filePath = job.filePath; QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", filePath, exportErrorToUserMessage(status, errorMessage))); } } else { if (!(job.flags & KritaUtils::SaveIsExporting)) { const QString existingAutoSaveBaseName = localFilePath(); const bool wasRecovered = isRecovered(); setUrl(QUrl::fromLocalFile(job.filePath)); setLocalFilePath(job.filePath); setMimeType(job.mimeType); updateEditingTime(true); if (!d->modifiedWhileSaving) { /** * If undo stack is already clean/empty, it doesn't emit any * signals, so we might forget update document modified state * (which was set, e.g. while recovering an autosave file) */ if (d->undoStack->isClean()) { setModified(false); } else { d->undoStack->setClean(); } } setRecovered(false); removeAutoSaveFiles(existingAutoSaveBaseName, wasRecovered); } emit completed(); emit sigSavingFinished(); emit statusBarMessage(i18n("Finished saving %1", fileName), successMessageTimeout); } } QByteArray KisDocument::mimeType() const { return d->mimeType; } void KisDocument::setMimeType(const QByteArray & mimeType) { d->mimeType = mimeType; } bool KisDocument::fileBatchMode() const { return d->batchMode; } void KisDocument::setFileBatchMode(const bool batchMode) { d->batchMode = batchMode; } KisDocument* KisDocument::lockAndCloneForSaving() { // force update of all the asynchronous nodes before cloning QApplication::processEvents(); KisLayerUtils::forceAllDelayedNodesUpdate(d->image->root()); KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window) { if (window->viewManager()) { if (!window->viewManager()->blockUntilOperationsFinished(d->image)) { return 0; } } } Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return 0; } return new KisDocument(*this); } KisDocument *KisDocument::lockAndCreateSnapshot() { KisDocument *doc = lockAndCloneForSaving(); if (doc) { // clone palette list doc->d->paletteList = doc->d->clonePaletteList(doc->d->paletteList); } return doc; } void KisDocument::copyFromDocument(const KisDocument &rhs) { copyFromDocumentImpl(rhs, REPLACE); } void KisDocument::copyFromDocumentImpl(const KisDocument &rhs, CopyPolicy policy) { if (policy == REPLACE) { d->copyFrom(*(rhs.d), this); d->undoStack->clear(); } else { // in CONSTRUCT mode, d should be already initialized connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(d->undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(slotUndoStackCleanChanged(bool))); connect(d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); d->shapeController = new KisShapeController(this, d->nserver); d->koShapeController = new KoShapeController(0, d->shapeController); d->shapeController->resourceManager()->setGlobalShapeController(d->koShapeController); } setObjectName(rhs.objectName()); slotConfigChanged(); if (rhs.d->image) { if (policy == REPLACE) { d->image->barrierLock(/* readOnly = */ false); rhs.d->image->barrierLock(/* readOnly = */ true); d->image->copyFromImage(*(rhs.d->image)); d->image->unlock(); rhs.d->image->unlock(); setCurrentImage(d->image, /* forceInitialUpdate = */ true); } else { // clone the image with keeping the GUIDs of the layers intact // NOTE: we expect the image to be locked! setCurrentImage(rhs.image()->clone(/* exactCopy = */ true), /* forceInitialUpdate = */ false); } } if (rhs.d->preActivatedNode) { QQueue linearizedNodes; KisLayerUtils::recursiveApplyNodes(rhs.d->image->root(), [&linearizedNodes](KisNodeSP node) { linearizedNodes.enqueue(node); }); KisLayerUtils::recursiveApplyNodes(d->image->root(), [&linearizedNodes, &rhs, this](KisNodeSP node) { KisNodeSP refNode = linearizedNodes.dequeue(); if (rhs.d->preActivatedNode.data() == refNode.data()) { d->preActivatedNode = node; } }); } KisNodeSP foundNode = KisLayerUtils::recursiveFindNode(image()->rootLayer(), [](KisNodeSP node) -> bool { return dynamic_cast(node.data()); }); KisReferenceImagesLayer *refLayer = dynamic_cast(foundNode.data()); setReferenceImagesLayer(refLayer, /* updateImage = */ false); if (policy == REPLACE) { setModified(true); } } bool KisDocument::exportDocumentSync(const QUrl &url, const QByteArray &mimeType, KisPropertiesConfigurationSP exportConfiguration) { { /** * The caller guarantees that noone else uses the document (usually, * it is a temporary docuent created specifically for exporting), so * we don't need to copy or lock the document. Instead we should just * ensure the barrier lock is synced and then released. */ Private::StrippedSafeSavingLocker locker(&d->savingMutex, d->image); if (!locker.successfullyLocked()) { return false; } } d->savingImage = d->image; const QString fileName = url.toLocalFile(); KisImportExportErrorCode status = d->importExportManager-> exportDocument(fileName, fileName, mimeType, false, exportConfiguration); d->savingImage = 0; return status.isOk(); } bool KisDocument::initiateSavingInBackground(const QString actionName, const QObject *receiverObject, const char *receiverMethod, const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration) { return initiateSavingInBackground(actionName, receiverObject, receiverMethod, job, exportConfiguration, std::unique_ptr()); } bool KisDocument::initiateSavingInBackground(const QString actionName, const QObject *receiverObject, const char *receiverMethod, const KritaUtils::ExportFileJob &job, KisPropertiesConfigurationSP exportConfiguration, std::unique_ptr &&optionalClonedDocument) { KIS_ASSERT_RECOVER_RETURN_VALUE(job.isValid(), false); QScopedPointer clonedDocument; if (!optionalClonedDocument) { clonedDocument.reset(lockAndCloneForSaving()); } else { clonedDocument.reset(optionalClonedDocument.release()); } // we block saving until the current saving is finished! if (!clonedDocument || !d->savingMutex.tryLock()) { return false; } auto waitForImage = [] (KisImageSP image) { KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window) { if (window->viewManager()) { window->viewManager()->blockUntilOperationsFinishedForced(image); } } }; { KisNodeSP newRoot = clonedDocument->image()->root(); KIS_SAFE_ASSERT_RECOVER(!KisLayerUtils::hasDelayedNodeWithUpdates(newRoot)) { KisLayerUtils::forceAllDelayedNodesUpdate(newRoot); waitForImage(clonedDocument->image()); } } KIS_SAFE_ASSERT_RECOVER(clonedDocument->image()->isIdle()) { waitForImage(clonedDocument->image()); } KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveDocument, false); KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveJob.isValid(), false); d->backgroundSaveDocument.reset(clonedDocument.take()); d->backgroundSaveJob = job; d->modifiedWhileSaving = false; if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = true; } connect(d->backgroundSaveDocument.data(), SIGNAL(sigBackgroundSavingFinished(KisImportExportErrorCode, QString)), this, SLOT(slotChildCompletedSavingInBackground(KisImportExportErrorCode, QString))); connect(this, SIGNAL(sigCompleteBackgroundSaving(KritaUtils::ExportFileJob, KisImportExportErrorCode, QString)), receiverObject, receiverMethod, Qt::UniqueConnection); bool started = d->backgroundSaveDocument->startExportInBackground(actionName, job.filePath, job.filePath, job.mimeType, job.flags & KritaUtils::SaveShowWarnings, exportConfiguration); if (!started) { // the state should have been deinitialized in slotChildCompletedSavingInBackground() KIS_SAFE_ASSERT_RECOVER (!d->backgroundSaveDocument && !d->backgroundSaveJob.isValid()) { d->backgroundSaveDocument.take()->deleteLater(); d->savingMutex.unlock(); d->backgroundSaveJob = KritaUtils::ExportFileJob(); } } return started; } void KisDocument::slotChildCompletedSavingInBackground(KisImportExportErrorCode status, const QString &errorMessage) { KIS_SAFE_ASSERT_RECOVER(!d->savingMutex.tryLock()) { d->savingMutex.unlock(); return; } KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveDocument); if (d->backgroundSaveJob.flags & KritaUtils::SaveInAutosaveMode) { d->backgroundSaveDocument->d->isAutosaving = false; } d->backgroundSaveDocument.take()->deleteLater(); d->savingMutex.unlock(); KIS_SAFE_ASSERT_RECOVER_RETURN(d->backgroundSaveJob.isValid()); const KritaUtils::ExportFileJob job = d->backgroundSaveJob; d->backgroundSaveJob = KritaUtils::ExportFileJob(); KisUsageLogger::log(QString("Completed saving %1 (mime: %2). Result: %3") .arg(job.filePath) .arg(QString::fromLatin1(job.mimeType)) .arg(!status.isOk() ? exportErrorToUserMessage(status, errorMessage) : "OK")); emit sigCompleteBackgroundSaving(job, status, errorMessage); } void KisDocument::slotAutoSaveImpl(std::unique_ptr &&optionalClonedDocument) { if (!d->modified || !d->modifiedAfterAutosave) return; const QString autoSaveFileName = generateAutoSaveFileName(localFilePath()); emit statusBarMessage(i18n("Autosaving... %1", autoSaveFileName), successMessageTimeout); const bool hadClonedDocument = bool(optionalClonedDocument); bool started = false; if (d->image->isIdle() || hadClonedDocument) { started = initiateSavingInBackground(i18n("Autosaving..."), this, SLOT(slotCompleteAutoSaving(KritaUtils::ExportFileJob, KisImportExportErrorCode, QString)), KritaUtils::ExportFileJob(autoSaveFileName, nativeFormatMimeType(), KritaUtils::SaveIsExporting | KritaUtils::SaveInAutosaveMode), 0, std::move(optionalClonedDocument)); } else { emit statusBarMessage(i18n("Autosaving postponed: document is busy..."), errorMessageTimeout); } if (!started && !hadClonedDocument && d->autoSaveFailureCount >= 3) { KisCloneDocumentStroke *stroke = new KisCloneDocumentStroke(this); connect(stroke, SIGNAL(sigDocumentCloned(KisDocument*)), this, SLOT(slotInitiateAsyncAutosaving(KisDocument*)), Qt::BlockingQueuedConnection); KisStrokeId strokeId = d->image->startStroke(stroke); d->image->endStroke(strokeId); setInfiniteAutoSaveInterval(); } else if (!started) { setEmergencyAutoSaveInterval(); } else { d->modifiedAfterAutosave = false; } } void KisDocument::slotAutoSave() { slotAutoSaveImpl(std::unique_ptr()); } void KisDocument::slotInitiateAsyncAutosaving(KisDocument *clonedDocument) { slotAutoSaveImpl(std::unique_ptr(clonedDocument)); } void KisDocument::slotCompleteAutoSaving(const KritaUtils::ExportFileJob &job, KisImportExportErrorCode status, const QString &errorMessage) { Q_UNUSED(job); const QString fileName = QFileInfo(job.filePath).fileName(); if (!status.isOk()) { setEmergencyAutoSaveInterval(); emit statusBarMessage(i18nc("%1 --- failing file name, %2 --- error message", "Error during autosaving %1: %2", fileName, exportErrorToUserMessage(status, errorMessage)), errorMessageTimeout); } else { KisConfig cfg(true); d->autoSaveDelay = cfg.autoSaveInterval(); if (!d->modifiedWhileSaving) { d->autoSaveTimer->stop(); // until the next change d->autoSaveFailureCount = 0; } else { setNormalAutoSaveInterval(); } emit statusBarMessage(i18n("Finished autosaving %1", fileName), successMessageTimeout); } } bool KisDocument::startExportInBackground(const QString &actionName, const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration) { d->savingImage = d->image; KisMainWindow *window = KisPart::instance()->currentMainwindow(); if (window) { if (window->viewManager()) { d->savingUpdater = window->viewManager()->createThreadedUpdater(actionName); d->importExportManager->setUpdater(d->savingUpdater); } } KisImportExportErrorCode initializationStatus(ImportExportCodes::OK); d->childSavingFuture = d->importExportManager->exportDocumentAsyc(location, realLocation, mimeType, initializationStatus, showWarnings, exportConfiguration); if (!initializationStatus.isOk()) { if (d->savingUpdater) { d->savingUpdater->cancel(); } d->savingImage.clear(); emit sigBackgroundSavingFinished(initializationStatus, initializationStatus.errorMessage()); return false; } typedef QFutureWatcher StatusWatcher; StatusWatcher *watcher = new StatusWatcher(); watcher->setFuture(d->childSavingFuture); connect(watcher, SIGNAL(finished()), SLOT(finishExportInBackground())); connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater())); return true; } void KisDocument::finishExportInBackground() { KIS_SAFE_ASSERT_RECOVER(d->childSavingFuture.isFinished()) { emit sigBackgroundSavingFinished(ImportExportCodes::InternalError, ""); return; } KisImportExportErrorCode status = d->childSavingFuture.result(); const QString errorMessage = status.errorMessage(); d->savingImage.clear(); d->childSavingFuture = QFuture(); d->lastErrorMessage.clear(); if (d->savingUpdater) { d->savingUpdater->setProgress(100); } emit sigBackgroundSavingFinished(status, errorMessage); } void KisDocument::setReadWrite(bool readwrite) { d->readwrite = readwrite; setNormalAutoSaveInterval(); Q_FOREACH (KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) { mainWindow->setReadWrite(readwrite); } } void KisDocument::setAutoSaveDelay(int delay) { if (isReadWrite() && delay > 0) { d->autoSaveTimer->start(delay * 1000); } else { d->autoSaveTimer->stop(); } } void KisDocument::setNormalAutoSaveInterval() { setAutoSaveDelay(d->autoSaveDelay); d->autoSaveFailureCount = 0; } void KisDocument::setEmergencyAutoSaveInterval() { const int emergencyAutoSaveInterval = 10; /* sec */ setAutoSaveDelay(emergencyAutoSaveInterval); d->autoSaveFailureCount++; } void KisDocument::setInfiniteAutoSaveInterval() { setAutoSaveDelay(-1); } KoDocumentInfo *KisDocument::documentInfo() const { return d->docInfo; } bool KisDocument::isModified() const { return d->modified; } QPixmap KisDocument::generatePreview(const QSize& size) { KisImageSP image = d->image; if (d->savingImage) image = d->savingImage; if (image) { QRect bounds = image->bounds(); QSize newSize = bounds.size(); newSize.scale(size, Qt::KeepAspectRatio); QPixmap px = QPixmap::fromImage(image->convertToQImage(newSize, 0)); if (px.size() == QSize(0,0)) { px = QPixmap(newSize); QPainter gc(&px); QBrush checkBrush = QBrush(KisCanvasWidgetBase::createCheckersImage(newSize.width() / 5)); gc.fillRect(px.rect(), checkBrush); gc.end(); } return px; } return QPixmap(size); } QString KisDocument::generateAutoSaveFileName(const QString & path) const { QString retval; // Using the extension allows to avoid relying on the mime magic when opening const QString extension (".kra"); QString prefix = KisConfig(true).readEntry("autosavefileshidden") ? QString(".") : QString(); QRegularExpression autosavePattern1("^\\..+-autosave.kra$"); QRegularExpression autosavePattern2("^.+-autosave.kra$"); QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); if (path.isEmpty() || autosavePattern1.match(filename).hasMatch() || autosavePattern2.match(filename).hasMatch() || !fi.isWritable()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) retval = QString("%1%2%7%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension).arg(prefix); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file retval = QString("%1%2%7%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension).arg(prefix); #endif } else { retval = QString("%1%2%5%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension).arg(prefix); } //qDebug() << "generateAutoSaveFileName() for path" << path << ":" << retval; return retval; } bool KisDocument::importDocument(const QUrl &_url) { bool ret; dbgUI << "url=" << _url.url(); // open... ret = openUrl(_url); // reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a // File --> Import if (ret) { dbgUI << "success, resetting url"; resetURL(); setTitleModified(); } return ret; } bool KisDocument::openUrl(const QUrl &_url, OpenFlags flags) { if (!_url.isLocalFile()) { return false; } dbgUI << "url=" << _url.url(); d->lastErrorMessage.clear(); // Reimplemented, to add a check for autosave files and to improve error reporting if (!_url.isValid()) { d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ? return false; } QUrl url(_url); bool autosaveOpened = false; if (url.isLocalFile() && !fileBatchMode()) { QString file = url.toLocalFile(); QString asf = generateAutoSaveFileName(file); if (QFile::exists(asf)) { KisApplication *kisApp = static_cast(qApp); kisApp->hideSplashScreen(); //dbgUI <<"asf=" << asf; // ## TODO compare timestamps ? int res = QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("An autosaved file exists for this document.\nDo you want to open the autosaved file instead?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : url.setPath(asf); autosaveOpened = true; break; case QMessageBox::No : QFile::remove(asf); break; default: // Cancel return false; } } } bool ret = openUrlInternal(url); if (autosaveOpened || flags & RecoveryFile) { setReadWrite(true); // enable save button setModified(true); setRecovered(true); } else { if (ret) { if (!(flags & DontAddToRecent)) { KisPart::instance()->addRecentURLToAllMainWindows(_url); } // Detect readonly local-files; remote files are assumed to be writable QFileInfo fi(url.toLocalFile()); setReadWrite(fi.isWritable()); } setRecovered(false); } return ret; } class DlgLoadMessages : public KoDialog { public: DlgLoadMessages(const QString &title, const QString &message, const QStringList &warnings) { setWindowTitle(title); setWindowIcon(KisIconUtils::loadIcon("warning")); QWidget *page = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(page); QHBoxLayout *hlayout = new QHBoxLayout(); QLabel *labelWarning= new QLabel(); labelWarning->setPixmap(KisIconUtils::loadIcon("warning").pixmap(32, 32)); hlayout->addWidget(labelWarning); hlayout->addWidget(new QLabel(message)); layout->addLayout(hlayout); QTextBrowser *browser = new QTextBrowser(); QString warning = "

"; if (warnings.size() == 1) { warning += " Reason:

"; } else { warning += " Reasons:

"; } warning += "

    "; Q_FOREACH(const QString &w, warnings) { warning += "\n
  • " + w + "
  • "; } warning += "
"; browser->setHtml(warning); browser->setMinimumHeight(200); browser->setMinimumWidth(400); layout->addWidget(browser); setMainWidget(page); setButtons(KoDialog::Ok); resize(minimumSize()); } }; bool KisDocument::openFile() { //dbgUI <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); return false; } QString filename = localFilePath(); QString typeName = mimeType(); if (typeName.isEmpty()) { typeName = KisMimeDatabase::mimeTypeForFile(filename); } //qDebug() << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = filename; while (path.length() > 0) { path.chop(1); typeName = KisMimeDatabase::mimeTypeForFile(path); //qDebug() << "\t" << path << typeName; if (!typeName.isEmpty()) { break; } } //qDebug() << "chopped" << filename << "to" << path << "Was trash, is" << typeName; } dbgUI << localFilePath() << "type:" << typeName; KisMainWindow *window = KisPart::instance()->currentMainwindow(); KoUpdaterPtr updater; if (window && window->viewManager()) { updater = window->viewManager()->createUnthreadedUpdater(i18n("Opening document")); d->importExportManager->setUpdater(updater); } KisImportExportErrorCode status = d->importExportManager->importDocument(localFilePath(), typeName); if (!status.isOk()) { if (window && window->viewManager()) { updater->cancel(); } QString msg = status.errorMessage(); if (!msg.isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("Could not open %2.\nReason: %1.", msg, prettyPathOrUrl()), errorMessage().split("\n") + warningMessage().split("\n")); dlg.exec(); } return false; } else if (!warningMessage().isEmpty()) { DlgLoadMessages dlg(i18nc("@title:window", "Krita"), i18n("There were problems opening %1.", prettyPathOrUrl()), warningMessage().split("\n")); dlg.exec(); setUrl(QUrl()); } setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); undoStack()->clear(); return true; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; } bool KisDocument::loadNativeFormat(const QString & file_) { return openUrl(QUrl::fromLocalFile(file_)); } void KisDocument::setModified(bool mod) { if (mod) { updateEditingTime(false); } if (d->isAutosaving) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<docInfo->aboutInfo("editing-time").toInt() + d->firstMod.secsTo(d->lastMod))); d->firstMod = now; } else if (firstModDelta > 60 || forceStoreElapsed) { d->docInfo->setAboutInfo("editing-time", QString::number(d->docInfo->aboutInfo("editing-time").toInt() + firstModDelta)); d->firstMod = now; } d->lastMod = now; } QString KisDocument::prettyPathOrUrl() const { QString _url(url().toDisplayString()); #ifdef Q_OS_WIN if (url().isLocalFile()) { _url = QDir::toNativeSeparators(_url); } #endif return _url; } // Get caption from document info (title(), in about page) QString KisDocument::caption() const { QString c; const QString _url(url().fileName()); // if URL is empty...it is probably an unsaved file if (_url.isEmpty()) { c = " [" + i18n("Not Saved") + "] "; } else { c = _url; // Fall back to document URL } return c; } void KisDocument::setTitleModified() { emit titleModified(caption(), isModified()); } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument("krita", tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::setWarningMessage(const QString& warningMsg) { d->lastWarningMessage = warningMsg; } QString KisDocument::warningMessage() const { return d->lastWarningMessage; } void KisDocument::removeAutoSaveFiles(const QString &autosaveBaseName, bool wasRecovered) { //qDebug() << "removeAutoSaveFiles"; // Eliminate any auto-save file QString asf = generateAutoSaveFileName(autosaveBaseName); // the one in the current dir //qDebug() << "\tfilename:" << asf << "exists:" << QFile::exists(asf); if (QFile::exists(asf)) { //qDebug() << "\tremoving autosavefile" << asf; QFile::remove(asf); } asf = generateAutoSaveFileName(QString()); // and the one in $HOME //qDebug() << "Autsavefile in $home" << asf; if (QFile::exists(asf)) { //qDebug() << "\tremoving autsavefile 2" << asf; QFile::remove(asf); } QList expressions; expressions << QRegularExpression("^\\..+-autosave.kra$") << QRegularExpression("^.+-autosave.kra$"); Q_FOREACH(const QRegularExpression &rex, expressions) { if (wasRecovered && !autosaveBaseName.isEmpty() && rex.match(QFileInfo(autosaveBaseName).fileName()).hasMatch() && QFile::exists(autosaveBaseName)) { QFile::remove(autosaveBaseName); } } } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } KisImportExportManager *KisDocument::importExportManager() const { return d->importExportManager; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackCleanChanged(bool value) { setModified(!value); } void KisDocument::slotConfigChanged() { KisConfig cfg(true); if (d->undoStack->undoLimit() != cfg.undoStackLimit()) { if (!d->undoStack->isClean()) { d->undoStack->clear(); } d->undoStack->setUndoLimit(cfg.undoStackLimit()); } d->autoSaveDelay = cfg.autoSaveInterval(); setNormalAutoSaveInterval(); } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KisGridConfig KisDocument::gridConfig() const { return d->gridConfig; } void KisDocument::setGridConfig(const KisGridConfig &config) { if (d->gridConfig != config) { d->gridConfig = config; emit sigGridConfigChanged(config); } } QList &KisDocument::paletteList() { return d->paletteList; } void KisDocument::setPaletteList(const QList &paletteList, bool emitSignal) { if (d->paletteList != paletteList) { QList oldPaletteList = d->paletteList; d->paletteList = paletteList; if (emitSignal) { emit sigPaletteListChanged(oldPaletteList, paletteList); } } } const KisGuidesConfig& KisDocument::guidesConfig() const { return d->guidesConfig; } void KisDocument::setGuidesConfig(const KisGuidesConfig &data) { if (d->guidesConfig == data) return; d->guidesConfig = data; emit sigGuidesConfigChanged(d->guidesConfig); } const KisMirrorAxisConfig& KisDocument::mirrorAxisConfig() const { return d->mirrorAxisConfig; } void KisDocument::setMirrorAxisConfig(const KisMirrorAxisConfig &config) { if (d->mirrorAxisConfig == config) { return; } d->mirrorAxisConfig = config; setModified(true); emit sigMirrorAxisConfigChanged(); } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( isReadWrite() && isModified()) { Q_FOREACH (KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } void KisDocument::setUrl(const QUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::openUrlInternal(const QUrl &url) { if ( !url.isValid() ) { return false; } if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) { return false; } d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); bool ret; // set the mimetype only if it was not already set (for example, by the host application) if (d->mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QString mime = KisMimeDatabase::mimeTypeForFile(d->m_url.toLocalFile()); d->mimeType = mime.toLocal8Bit(); d->m_bAutoDetectedMime = true; } setUrl(d->m_url); ret = openFile(); if (ret) { emit completed(); } else { emit canceled(QString()); } return ret; } return false; } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, KisConfig::BackgroundStyle bgStyle, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); KisImageSP image; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); documentInfo()->setAboutInfo("abstract", description); KisLayerSP layer; if (bgStyle == KisConfig::RASTER_LAYER || bgStyle == KisConfig::FILL_LAYER) { KoColor strippedAlpha = bgColor; strippedAlpha.setOpacity(OPACITY_OPAQUE_U8); if (bgStyle == KisConfig::RASTER_LAYER) { layer = new KisPaintLayer(image.data(), "Background", OPACITY_OPAQUE_U8, cs);; layer->paintDevice()->setDefaultPixel(strippedAlpha); } else if (bgStyle == KisConfig::FILL_LAYER) { KisFilterConfigurationSP filter_config = KisGeneratorRegistry::instance()->get("color")->defaultConfiguration(); filter_config->setProperty("color", strippedAlpha.toQColor()); layer = new KisGeneratorLayer(image.data(), "Background Fill", filter_config, image->globalSelection()); } layer->setOpacity(bgColor.opacityU8()); if (numberOfLayers > 1) { //Lock bg layer if others are present. layer->setUserLocked(true); } } else { // KisConfig::CANVAS_COLOR (needs an unlocked starting layer). image->setDefaultProjectionColor(bgColor); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); } Q_CHECK_PTR(layer); image->addNode(layer.data(), image->rootLayer().data()); layer->setDirty(QRect(0, 0, width, height)); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } KisConfig cfg(false); cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); KisUsageLogger::log(i18n("Created image \"%1\", %2 * %3 pixels, %4 dpi. Color model: %6 %5 (%7). Layers: %8" , name , width, height , imageResolution * 72.0 , image->colorSpace()->colorModelId().name(), image->colorSpace()->colorDepthId().name() , image->colorSpace()->profile()->name() , numberOfLayers)); QApplication::restoreOverrideCursor(); return true; } bool KisDocument::isSaving() const { const bool result = d->savingMutex.tryLock(); if (result) { d->savingMutex.unlock(); } return !result; } void KisDocument::waitForSavingToComplete() { if (isSaving()) { KisAsyncActionFeedback f(i18nc("progress dialog message when the user closes the document that is being saved", "Waiting for saving to complete..."), 0); f.waitForMutex(&d->savingMutex); } } KoShapeControllerBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } QList KisDocument::assistants() const { return d->assistants; } void KisDocument::setAssistants(const QList &value) { if (d->assistants != value) { d->assistants = value; emit sigAssistantsChanged(); } } KisSharedPtr KisDocument::referenceImagesLayer() const { return d->referenceImagesLayer.data(); } void KisDocument::setReferenceImagesLayer(KisSharedPtr layer, bool updateImage) { if (d->referenceImagesLayer == layer) { return; } if (d->referenceImagesLayer) { d->referenceImagesLayer->disconnect(this); } if (updateImage) { if (layer) { d->image->addNode(layer); } else { d->image->removeNode(d->referenceImagesLayer); } } d->referenceImagesLayer = layer; if (d->referenceImagesLayer) { connect(d->referenceImagesLayer, SIGNAL(sigUpdateCanvas(QRectF)), this, SIGNAL(sigReferenceImagesChanged())); } emit sigReferenceImagesLayerChanged(layer); } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } KisImageWSP KisDocument::image() const { return d->image; } KisImageSP KisDocument::savingImage() const { return d->savingImage; } void KisDocument::setCurrentImage(KisImageSP image, bool forceInitialUpdate) { if (d->image) { // Disconnect existing sig/slot connections d->image->setUndoStore(new KisDumbUndoStore()); d->image->disconnect(this); d->shapeController->setImage(0); d->image = 0; } if (!image) return; d->setImageAndInitIdleWatcher(image); d->image->setUndoStore(new KisDocumentUndoStore(this)); d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified()), Qt::UniqueConnection); if (forceInitialUpdate) { d->image->initialRefreshGraph(); } } void KisDocument::hackPreliminarySetImage(KisImageSP image) { KIS_SAFE_ASSERT_RECOVER_RETURN(!d->image); d->setImageAndInitIdleWatcher(image); d->shapeController->setImage(image); } void KisDocument::setImageModified() { - setModified(true); + // we only set as modified if undo stack is not at clean state + setModified(!d->undoStack->isClean()); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } bool KisDocument::isAutosaving() const { return d->isAutosaving; } QString KisDocument::exportErrorToUserMessage(KisImportExportErrorCode status, const QString &errorMessage) { return errorMessage.isEmpty() ? status.errorMessage() : errorMessage; } void KisDocument::setAssistantsGlobalColor(QColor color) { d->globalAssistantsColor = color; } QColor KisDocument::assistantsGlobalColor() { return d->globalAssistantsColor; } QRectF KisDocument::documentBounds() const { QRectF bounds = d->image->bounds(); if (d->referenceImagesLayer) { bounds |= d->referenceImagesLayer->boundingImageRect(); } return bounds; } diff --git a/libs/ui/KisReferenceImagesDecoration.cpp b/libs/ui/KisReferenceImagesDecoration.cpp index 4977d69335..e7dee87753 100644 --- a/libs/ui/KisReferenceImagesDecoration.cpp +++ b/libs/ui/KisReferenceImagesDecoration.cpp @@ -1,178 +1,180 @@ /* * Copyright (C) 2016 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisReferenceImagesDecoration.h" #include "KoShapeManager.h" #include "kis_algebra_2d.h" #include "KisDocument.h" #include "KisReferenceImagesLayer.h" struct KisReferenceImagesDecoration::Private { struct Buffer { /// Top left corner of the buffer relative to the viewport QPointF position; QImage image; QRectF bounds() const { return QRectF(position, image.size()); } }; KisReferenceImagesDecoration *q; KisWeakSharedPtr layer; Buffer buffer; QTransform previousTransform; QSizeF previousViewSize; explicit Private(KisReferenceImagesDecoration *q) : q(q) {} void updateBufferByImageCoordinates(const QRectF &dirtyImageRect) { QRectF dirtyWidgetRect = q->view()->viewConverter()->imageToWidget(dirtyImageRect); updateBuffer(dirtyWidgetRect, dirtyImageRect); } void updateBufferByWidgetCoordinates(const QRectF &dirtyWidgetRect) { QRectF dirtyImageRect = q->view()->viewConverter()->widgetToImage(dirtyWidgetRect); updateBuffer(dirtyWidgetRect, dirtyImageRect); } private: void updateBuffer(QRectF widgetRect, QRectF imageRect) { KisCoordinatesConverter *viewConverter = q->view()->viewConverter(); QTransform transform = viewConverter->imageToWidgetTransform(); if (buffer.image.isNull() || !buffer.bounds().contains(widgetRect)) { const QRectF boundingImageRect = layer->boundingImageRect(); const QRectF boundingWidgetRect = q->view()->viewConverter()->imageToWidget(boundingImageRect); widgetRect = boundingWidgetRect.intersected(q->view()->rect()); if (widgetRect.isNull()) return; buffer.position = widgetRect.topLeft(); buffer.image = QImage(widgetRect.size().toSize(), QImage::Format_ARGB32); buffer.image.fill(Qt::transparent); imageRect = q->view()->viewConverter()->widgetToImage(widgetRect); } QPainter gc(&buffer.image); gc.translate(-buffer.position); gc.setTransform(transform, true); gc.save(); gc.setCompositionMode(QPainter::CompositionMode_Source); gc.fillRect(imageRect, Qt::transparent); gc.restore(); gc.setClipRect(imageRect); layer->paintReferences(gc); } }; KisReferenceImagesDecoration::KisReferenceImagesDecoration(QPointer parent, KisDocument *document) : KisCanvasDecoration("referenceImagesDecoration", parent) , d(new Private(this)) { connect(document->image().data(), SIGNAL(sigNodeAddedAsync(KisNodeSP)), this, SLOT(slotNodeAdded(KisNodeSP))); connect(document, &KisDocument::sigReferenceImagesLayerChanged, this, &KisReferenceImagesDecoration::slotNodeAdded); auto referenceImageLayer = document->referenceImagesLayer(); if (referenceImageLayer) { setReferenceImageLayer(referenceImageLayer); } } KisReferenceImagesDecoration::~KisReferenceImagesDecoration() {} void KisReferenceImagesDecoration::addReferenceImage(KisReferenceImage *referenceImage) { KisDocument *document = view()->document(); KUndo2Command *cmd = KisReferenceImagesLayer::addReferenceImages(document, {referenceImage}); document->addCommand(cmd); } bool KisReferenceImagesDecoration::documentHasReferenceImages() const { return view()->document()->referenceImagesLayer() != nullptr; } void KisReferenceImagesDecoration::drawDecoration(QPainter &gc, const QRectF &/*updateRect*/, const KisCoordinatesConverter *converter, KisCanvas2 */*canvas*/) { // TODO: can we use partial updates here? KisSharedPtr layer = d->layer.toStrongRef(); if (!layer.isNull()) { QSizeF viewSize = view()->size(); QTransform transform = converter->imageToWidgetTransform(); if (d->previousViewSize != viewSize || !KisAlgebra2D::fuzzyMatrixCompare(transform, d->previousTransform, 1e-4)) { d->previousViewSize = viewSize; d->previousTransform = transform; d->buffer.image = QImage(); d->updateBufferByWidgetCoordinates(QRectF(QPointF(0,0), viewSize)); } if (!d->buffer.image.isNull()) { gc.drawImage(d->buffer.position, d->buffer.image); } } } void KisReferenceImagesDecoration::slotNodeAdded(KisNodeSP node) { auto *referenceImagesLayer = dynamic_cast(node.data()); if (referenceImagesLayer) { setReferenceImageLayer(referenceImagesLayer); } } void KisReferenceImagesDecoration::slotReferenceImagesChanged(const QRectF &dirtyRect) { d->updateBufferByImageCoordinates(dirtyRect); QRectF documentRect = view()->viewConverter()->imageToDocument(dirtyRect); view()->canvasBase()->updateCanvas(documentRect); } void KisReferenceImagesDecoration::setReferenceImageLayer(KisSharedPtr layer) { if (d->layer.data() != layer.data()) { if (d->layer) { d->layer->disconnect(this); } d->layer = layer; connect(layer.data(), SIGNAL(sigUpdateCanvas(QRectF)), this, SLOT(slotReferenceImagesChanged(QRectF))); - slotReferenceImagesChanged(layer->extent()); + if (layer->extent() != QRectF()) { // in case the reference layer is just being loaded from the .kra file + slotReferenceImagesChanged(layer->extent()); + } } } diff --git a/libs/ui/canvas/KisMirrorAxisConfig.cpp b/libs/ui/canvas/KisMirrorAxisConfig.cpp index 2a316f9ee5..c99e74f133 100644 --- a/libs/ui/canvas/KisMirrorAxisConfig.cpp +++ b/libs/ui/canvas/KisMirrorAxisConfig.cpp @@ -1,250 +1,251 @@ /* * Copyright (c) 2019 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "KisMirrorAxisConfig.h" class Q_DECL_HIDDEN KisMirrorAxisConfig::Private { public: Private() : mirrorHorizontal(false) , mirrorVertical(false) , lockHorizontal(false) , lockVertical(false) , hideVerticalDecoration(false) , hideHorizontalDecoration(false) , handleSize(32.f) , horizontalHandlePosition(64.f) , verticalHandlePosition(64.f) , axisPosition(QPointF(0.f,0.f)) {} bool operator==(const Private& rhs) { return mirrorHorizontal == rhs.mirrorHorizontal && mirrorVertical == rhs.mirrorVertical && lockHorizontal == rhs.lockHorizontal && lockVertical == rhs.lockVertical && hideHorizontalDecoration == rhs.hideHorizontalDecoration && hideVerticalDecoration == rhs.hideVerticalDecoration && handleSize == rhs.handleSize && horizontalHandlePosition == rhs.horizontalHandlePosition && verticalHandlePosition == rhs.verticalHandlePosition && axisPosition == rhs.axisPosition; } bool mirrorHorizontal; bool mirrorVertical; bool lockHorizontal; bool lockVertical; bool hideVerticalDecoration; bool hideHorizontalDecoration; float handleSize; float horizontalHandlePosition; float verticalHandlePosition; QPointF axisPosition; }; KisMirrorAxisConfig::KisMirrorAxisConfig() : QObject() , d(new Private()) { } KisMirrorAxisConfig::~KisMirrorAxisConfig() { } KisMirrorAxisConfig::KisMirrorAxisConfig(const KisMirrorAxisConfig &rhs) : QObject() , d(new Private(*rhs.d)) { } KisMirrorAxisConfig &KisMirrorAxisConfig::operator=(const KisMirrorAxisConfig &rhs) { if (&rhs != this) { *d = *rhs.d; } return *this; } bool KisMirrorAxisConfig::operator==(const KisMirrorAxisConfig &rhs) const { KIS_ASSERT(d); KIS_ASSERT(rhs.d); return *d == *rhs.d; } bool KisMirrorAxisConfig::mirrorHorizontal() { return d->mirrorHorizontal; } void KisMirrorAxisConfig::setMirrorHorizontal(bool state) { d->mirrorHorizontal = state; } bool KisMirrorAxisConfig::mirrorVertical() { return d->mirrorVertical; } void KisMirrorAxisConfig::setMirrorVertical(bool state) { d->mirrorVertical = state; } bool KisMirrorAxisConfig::lockHorizontal() { return d->lockHorizontal; } void KisMirrorAxisConfig::setLockHorizontal(bool state) { d->lockHorizontal = state; } bool KisMirrorAxisConfig::lockVertical() { return d->lockVertical; } void KisMirrorAxisConfig::setLockVertical(bool state) { d->lockVertical = state; } bool KisMirrorAxisConfig::hideVerticalDecoration() { return d->hideVerticalDecoration; } void KisMirrorAxisConfig::setHideVerticalDecoration(bool state) { d->hideVerticalDecoration = state; } bool KisMirrorAxisConfig::hideHorizontalDecoration() { return d->hideHorizontalDecoration; } void KisMirrorAxisConfig::setHideHorizontalDecoration(bool state) { d->hideHorizontalDecoration = state; } float KisMirrorAxisConfig::handleSize() { return d->handleSize; } void KisMirrorAxisConfig::setHandleSize(float size) { d->handleSize = size; } float KisMirrorAxisConfig::horizontalHandlePosition() { return d->horizontalHandlePosition; } void KisMirrorAxisConfig::setHorizontalHandlePosition(float position) { d->horizontalHandlePosition = position; } float KisMirrorAxisConfig::verticalHandlePosition() { return d->verticalHandlePosition; } void KisMirrorAxisConfig::setVerticalHandlePosition(float position) { d->verticalHandlePosition = position; } QPointF KisMirrorAxisConfig::axisPosition() { return d->axisPosition; } void KisMirrorAxisConfig::setAxisPosition(QPointF position) { d->axisPosition = position; } QDomElement KisMirrorAxisConfig::saveToXml(QDomDocument &doc, const QString &tag) const { QDomElement mirrorAxisElement = doc.createElement(tag); KisDomUtils::saveValue(&mirrorAxisElement, "mirrorHorizontal", d->mirrorHorizontal); KisDomUtils::saveValue(&mirrorAxisElement, "mirrorVertical", d->mirrorVertical); KisDomUtils::saveValue(&mirrorAxisElement, "lockHorizontal", d->lockHorizontal); KisDomUtils::saveValue(&mirrorAxisElement, "lockVertical", d->lockVertical); KisDomUtils::saveValue(&mirrorAxisElement, "hideHorizontalDecoration", d->hideHorizontalDecoration); KisDomUtils::saveValue(&mirrorAxisElement, "hideVerticalDecoration", d->hideVerticalDecoration); KisDomUtils::saveValue(&mirrorAxisElement, "handleSize", d->handleSize); KisDomUtils::saveValue(&mirrorAxisElement, "horizontalHandlePosition", d->horizontalHandlePosition); KisDomUtils::saveValue(&mirrorAxisElement, "verticalHandlePosition", d->verticalHandlePosition); KisDomUtils::saveValue(&mirrorAxisElement, "axisPosition", d->axisPosition); return mirrorAxisElement; } bool KisMirrorAxisConfig::loadFromXml(const QDomElement &parent) { bool result = true; result &= KisDomUtils::loadValue(parent, "mirrorHorizontal", &d->mirrorHorizontal); result &= KisDomUtils::loadValue(parent, "mirrorVertical", &d->mirrorVertical); result &= KisDomUtils::loadValue(parent, "lockHorizontal", &d->lockHorizontal); result &= KisDomUtils::loadValue(parent, "lockVertical", &d->lockVertical); result &= KisDomUtils::loadValue(parent, "hideHorizontalDecoration", &d->hideHorizontalDecoration); result &= KisDomUtils::loadValue(parent, "hideVerticalDecoration", &d->hideVerticalDecoration); result &= KisDomUtils::loadValue(parent, "handleSize", &d->handleSize); result &= KisDomUtils::loadValue(parent, "horizontalHandlePosition", &d->horizontalHandlePosition); result &= KisDomUtils::loadValue(parent, "verticalHandlePosition", &d->verticalHandlePosition); result &= KisDomUtils::loadValue(parent, "axisPosition", &d->axisPosition); return result; } bool KisMirrorAxisConfig::isDefault() const { KisMirrorAxisConfig defaultConfig; return *this == defaultConfig; } diff --git a/libs/ui/canvas/KisMirrorAxisConfig.h b/libs/ui/canvas/KisMirrorAxisConfig.h index d2421297c3..53cb3ab075 100644 --- a/libs/ui/canvas/KisMirrorAxisConfig.h +++ b/libs/ui/canvas/KisMirrorAxisConfig.h @@ -1,103 +1,104 @@ /* * Copyright (c) 2019 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISMIRRORAXISCONFIG_H #define KISMIRRORAXISCONFIG_H #include #include "kritaui_export.h" #include class QDomElement; class QDomDocument; /** * @brief The KisMirrorAxisConfig class stores configuration for the KisMirrorAxis * canvas decoration. Contents are saved to/loaded from KRA documents. */ class KRITAUI_EXPORT KisMirrorAxisConfig : public QObject, boost::equality_comparable { Q_OBJECT public: KisMirrorAxisConfig(); ~KisMirrorAxisConfig(); KisMirrorAxisConfig(const KisMirrorAxisConfig &rhs); KisMirrorAxisConfig& operator=(const KisMirrorAxisConfig& rhs); bool operator==(const KisMirrorAxisConfig& rhs) const; bool mirrorHorizontal(); void setMirrorHorizontal(bool state); bool mirrorVertical(); void setMirrorVertical(bool state); bool lockHorizontal(); void setLockHorizontal(bool state); bool lockVertical(); void setLockVertical(bool state); bool hideVerticalDecoration(); void setHideVerticalDecoration(bool state); bool hideHorizontalDecoration(); void setHideHorizontalDecoration(bool state); float handleSize(); void setHandleSize(float size); float horizontalHandlePosition(); void setHorizontalHandlePosition(float position); float verticalHandlePosition(); void setVerticalHandlePosition(float position); QPointF axisPosition(); void setAxisPosition(QPointF position); /** * @brief saveToXml() function for KisKraSaver * @param doc * @param tag * @return */ QDomElement saveToXml(QDomDocument& doc, const QString &tag) const; /** * @brief loadFromXml() function for KisKraLoader * @param parent element * @return */ bool loadFromXml(const QDomElement &parent); /** * @brief Check whether the config object was changed, or is the class default. * @return true, if the object is default; false, if the config was changed */ bool isDefault() const; private: class Private; const QScopedPointer d; }; #endif // KISMIRRORAXISCONFIG_H diff --git a/libs/ui/canvas/KisSnapPixelStrategy.cpp b/libs/ui/canvas/KisSnapPixelStrategy.cpp index d924e19962..cdb79db7a6 100644 --- a/libs/ui/canvas/KisSnapPixelStrategy.cpp +++ b/libs/ui/canvas/KisSnapPixelStrategy.cpp @@ -1,56 +1,57 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisSnapPixelStrategy.h" #include #include "kis_global.h" #include "kis_canvas2.h" #include "KoSnapProxy.h" KisSnapPixelStrategy::KisSnapPixelStrategy(KoSnapGuide::Strategy type): KoSnapStrategy(type) { } KisSnapPixelStrategy::~KisSnapPixelStrategy() { } bool KisSnapPixelStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance) { Q_UNUSED(maxSnapDistance); KisCanvas2 *canvas2 = dynamic_cast(proxy->canvas()); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(canvas2, false); const QPointF imagePos = canvas2->coordinatesConverter()->documentToImage(mousePosition); const QPointF alignedDocPoint = canvas2->coordinatesConverter()->imageToDocument(imagePos.toPoint()); setSnappedPosition(alignedDocPoint); return true; } QPainterPath KisSnapPixelStrategy::decoration(const KoViewConverter &converter) const { QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5)); QPainterPath decoration; decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), 0)); decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), 0)); decoration.moveTo(snappedPosition() - QPointF(0, unzoomedSize.height())); decoration.lineTo(snappedPosition() + QPointF(0, unzoomedSize.height())); return decoration; } diff --git a/libs/ui/canvas/kis_mirror_axis.cpp b/libs/ui/canvas/kis_mirror_axis.cpp index c226b64205..71d5c4935e 100644 --- a/libs/ui/canvas/kis_mirror_axis.cpp +++ b/libs/ui/canvas/kis_mirror_axis.cpp @@ -1,466 +1,469 @@ /* * Copyright (c) 2014 Arjen Hiemstra * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "kis_mirror_axis.h" #include "KoConfig.h" #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_canvas_resource_provider.h" #include "KisViewManager.h" #include "KisView.h" #include "kis_image.h" #include "canvas/kis_canvas_controller.h" #include "input/kis_input_manager.h" #include "kis_algebra_2d.h" #include #include #include class KisMirrorAxis::Private { public: Private(KisMirrorAxis* qq) : q(qq) , resourceProvider(0) , xActive(false) , yActive(false) , sideMargin(10.f) , minHandlePosition(10.f + 32.f) , horizontalContainsCursor(false) , verticalContainsCursor(false) , horizontalAxis(QLineF()) , verticalAxis(QLineF()) , config(KisMirrorAxisConfig()) { } void setAxisPosition(float x, float y); void recomputeVisibleAxes(QRect viewport); KisMirrorAxis* q; KisCanvasResourceProvider* resourceProvider; KisImageWSP image; QPixmap horizontalHandleIcon; QPixmap verticalHandleIcon; QPixmap horizontalIcon; QPixmap verticalIcon; QRectF horizontalHandle; QRectF verticalHandle; bool xActive; bool yActive; float sideMargin; float minHandlePosition; bool horizontalContainsCursor; bool verticalContainsCursor; QLineF horizontalAxis; QLineF verticalAxis; KisMirrorAxisConfig config; }; KisMirrorAxis::KisMirrorAxis(KisCanvasResourceProvider* provider, QPointerparent) : KisCanvasDecoration("mirror_axis", parent) , d(new Private(this)) { d->resourceProvider = provider; connect(d->resourceProvider, SIGNAL(mirrorModeChanged()), SLOT(mirrorModeChanged())); connect(d->resourceProvider, SIGNAL(moveMirrorVerticalCenter()), SLOT(moveVerticalAxisToCenter())); connect(d->resourceProvider, SIGNAL(moveMirrorHorizontalCenter()), SLOT(moveHorizontalAxisToCenter())); d->config.setMirrorHorizontal(d->resourceProvider->mirrorHorizontal()); d->config.setMirrorVertical(d->resourceProvider->mirrorVertical()); d->horizontalIcon = KisIconUtils::loadIcon("mirrorAxis-HorizontalMove").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); d->verticalIcon = KisIconUtils::loadIcon("mirrorAxis-VerticalMove").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); d->horizontalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); d->verticalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); setVisible(d->config.mirrorHorizontal() || d->config.mirrorVertical()); d->image = parent->canvasBase()->image(); } KisMirrorAxis::~KisMirrorAxis() { } float KisMirrorAxis::handleSize() const { return d->config.handleSize(); } void KisMirrorAxis::setHandleSize(float newSize) { if(d->config.handleSize() != newSize) { d->config.setHandleSize(newSize); d->horizontalIcon = KisIconUtils::loadIcon("symmetry-horizontal").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); d->verticalIcon = KisIconUtils::loadIcon("symmetry-vertical").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); d->horizontalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); d->verticalHandleIcon = KisIconUtils::loadIcon("transform-move").pixmap(d->config.handleSize(), QIcon::Normal, QIcon::On); d->minHandlePosition = d->sideMargin + newSize; emit handleSizeChanged(); emit sigConfigChanged(); } } void KisMirrorAxis::drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter* converter, KisCanvas2* canvas) { Q_UNUSED(updateArea); Q_UNUSED(converter); Q_UNUSED(canvas); if (!view()->isCurrent()) { return; } gc.save(); gc.setPen(QPen(QColor(0, 0, 0, 128), 1)); gc.setBrush(Qt::white); gc.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); QOpenGLContext *ctx = QOpenGLContext::currentContext(); - bool hasMultisample = ((gc.paintEngine()->type() == QPaintEngine::OpenGL2) && - (ctx->hasExtension("GL_ARB_multisample"))); - - // QPainter cannot anti-alias the edges of circles etc. when using OpenGL - // So instead, use native OpenGL anti-aliasing when available. - if (hasMultisample) { - gc.beginNativePainting(); - ctx->functions()->glEnable(GL_MULTISAMPLE); - gc.endNativePainting(); + bool hasMultisample = false; + if (ctx) { + hasMultisample = ((gc.paintEngine()->type() == QPaintEngine::OpenGL2) && + (ctx->hasExtension("GL_ARB_multisample"))); + + // QPainter cannot anti-alias the edges of circles etc. when using OpenGL + // So instead, use native OpenGL anti-aliasing when available. + if (hasMultisample) { + gc.beginNativePainting(); + ctx->functions()->glEnable(GL_MULTISAMPLE); + gc.endNativePainting(); + } } float halfHandleSize = d->config.handleSize() / 2; d->recomputeVisibleAxes(gc.viewport()); if(d->config.mirrorHorizontal() && !d->config.hideHorizontalDecoration()) { if (!d->horizontalAxis.isNull()) { - // QPointF horizontalIndicatorCenter = d->horizontalAxis.unitVector().pointAt(15); - // QRectF horizontalIndicator = QRectF(horizontalIndicatorCenter.x() - halfHandleSize, horizontalIndicatorCenter.y() - halfHandleSize, d->config.handleSize(), d->config.handleSize()); + // QPointF horizontalIndicatorCenter = d->horizontalAxis.unitVector().pointAt(15); + // QRectF horizontalIndicator = QRectF(horizontalIndicatorCenter.x() - halfHandleSize, horizontalIndicatorCenter.y() - halfHandleSize, d->config.handleSize(), d->config.handleSize()); float horizontalHandlePosition = qBound(d->minHandlePosition, d->config.horizontalHandlePosition(), d->horizontalAxis.length() - d->minHandlePosition); QPointF horizontalHandleCenter = d->horizontalAxis.unitVector().pointAt(horizontalHandlePosition); d->horizontalHandle = QRectF(horizontalHandleCenter.x() - halfHandleSize, horizontalHandleCenter.y() - halfHandleSize, d->config.handleSize(), d->config.handleSize()); gc.setPen(QPen(QColor(0, 0, 0, 64), 2, Qt::DashDotDotLine, Qt::RoundCap, Qt::RoundJoin)); gc.drawLine(d->horizontalAxis); - // gc.drawEllipse(horizontalIndicator); - // gc.drawPixmap(horizontalIndicator.adjusted(5, 5, -5, -5).toRect(), d->horizontalIcon); + // gc.drawEllipse(horizontalIndicator); + // gc.drawPixmap(horizontalIndicator.adjusted(5, 5, -5, -5).toRect(), d->horizontalIcon); // don't draw the handles if we are locking the axis for movement if (!d->config.lockHorizontal()) { gc.setPen(QPen(QColor(0, 0, 0, 128), 2)); gc.drawEllipse(d->horizontalHandle); gc.drawPixmap(d->horizontalHandle.adjusted(5, 5, -5, -5).toRect(), d->horizontalIcon); } } else { d->horizontalHandle = QRectF(); } } if(d->config.mirrorVertical() && !d->config.hideVerticalDecoration()) { if (!d->verticalAxis.isNull()) { gc.setPen(QPen(QColor(0, 0, 0, 64), 2, Qt::DashDotDotLine, Qt::RoundCap, Qt::RoundJoin)); gc.drawLine(d->verticalAxis); - // QPointF verticalIndicatorCenter = d->verticalAxis.unitVector().pointAt(15); - // QRectF verticalIndicator = QRectF(verticalIndicatorCenter.x() - halfHandleSize, verticalIndicatorCenter.y() - halfHandleSize, d->config.handleSize(), d->config.handleSize()); + // QPointF verticalIndicatorCenter = d->verticalAxis.unitVector().pointAt(15); + // QRectF verticalIndicator = QRectF(verticalIndicatorCenter.x() - halfHandleSize, verticalIndicatorCenter.y() - halfHandleSize, d->config.handleSize(), d->config.handleSize()); float verticalHandlePosition = qBound(d->minHandlePosition, d->config.verticalHandlePosition(), d->verticalAxis.length() - d->minHandlePosition); QPointF verticalHandleCenter = d->verticalAxis.unitVector().pointAt(verticalHandlePosition); d->verticalHandle = QRectF(verticalHandleCenter.x() - halfHandleSize, verticalHandleCenter.y() - halfHandleSize, d->config.handleSize(), d->config.handleSize()); // don't draw the handles if we are locking the axis for movement if (!d->config.lockVertical()) { gc.setPen(QPen(QColor(0, 0, 0, 128), 2)); gc.drawEllipse(d->verticalHandle); gc.drawPixmap(d->verticalHandle.adjusted(5, 5, -5, -5).toRect(), d->verticalIcon); } } else { d->verticalHandle = QRectF(); } } if (hasMultisample) { gc.beginNativePainting(); ctx->functions()->glDisable(GL_MULTISAMPLE); gc.endNativePainting(); } gc.restore(); } bool KisMirrorAxis::eventFilter(QObject* target, QEvent* event) { if (!visible()) return false; QObject *expectedCanvasWidget = view() ? - view()->canvasBase()->canvasWidget() : 0; + view()->canvasBase()->canvasWidget() : 0; if (!expectedCanvasWidget || target != expectedCanvasWidget) return false; if(event->type() == QEvent::MouseButtonPress || event->type() == QEvent::TabletPress) { QMouseEvent *me = dynamic_cast(event); QTabletEvent *te = dynamic_cast(event); QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77)); if(d->config.mirrorHorizontal() && d->horizontalHandle.contains(pos) && !d->config.lockHorizontal() && !d->config.hideHorizontalDecoration() ) { d->xActive = true; QApplication::setOverrideCursor(Qt::ClosedHandCursor); event->accept(); return true; } if(d->config.mirrorVertical() && d->verticalHandle.contains(pos) && !d->config.lockVertical() && !d->config.hideVerticalDecoration()) { d->yActive = true; QApplication::setOverrideCursor(Qt::ClosedHandCursor); event->accept(); return true; } } if(event->type() == QEvent::MouseMove || event->type() == QEvent::TabletMove) { QMouseEvent *me = dynamic_cast(event); QTabletEvent *te = dynamic_cast(event); QPoint pos = me ? me->pos() : (te ? te->pos() : QPoint(77,77)); if(d->xActive) { float axisX = view()->viewConverter()->widgetToImage(pos).x(); d->setAxisPosition(axisX, d->config.axisPosition().y()); d->config.setHorizontalHandlePosition(KisAlgebra2D::dotProduct(pos - d->horizontalAxis.p1(), d->horizontalAxis.unitVector().p2() - d->horizontalAxis.p1())); emit sigConfigChanged(); event->accept(); return true; } if(d->yActive) { float axisY = view()->viewConverter()->widgetToImage(pos).y(); d->setAxisPosition(d->config.axisPosition().x(), axisY); d->config.setVerticalHandlePosition(KisAlgebra2D::dotProduct(pos - d->verticalAxis.p1(), d->verticalAxis.unitVector().p2() - d->verticalAxis.p1())); emit sigConfigChanged(); event->accept(); return true; } if(d->config.mirrorHorizontal() && !d->config.hideHorizontalDecoration()) { if(d->horizontalHandle.contains(pos) && !d->config.lockHorizontal()) { if(!d->horizontalContainsCursor) { QApplication::setOverrideCursor(Qt::OpenHandCursor); d->horizontalContainsCursor = true; } } else if(d->horizontalContainsCursor) { QApplication::restoreOverrideCursor(); d->horizontalContainsCursor = false; } } if(d->config.mirrorVertical() && !d->config.hideVerticalDecoration()) { if(d->verticalHandle.contains(pos) && !d->config.lockVertical()) { if(!d->verticalContainsCursor) { QApplication::setOverrideCursor(Qt::OpenHandCursor); d->verticalContainsCursor = true; } } else if(d->verticalContainsCursor) { QApplication::restoreOverrideCursor(); d->verticalContainsCursor = false; } } } if(event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::TabletRelease) { if(d->xActive) { QApplication::restoreOverrideCursor(); d->xActive = false; event->accept(); return true; } if(d->yActive) { QApplication::restoreOverrideCursor(); d->yActive = false; event->accept(); return true; } } return QObject::eventFilter(target, event); } void KisMirrorAxis::mirrorModeChanged() { if (!view()->isCurrent()) { return; } d->config.setMirrorHorizontal(d->resourceProvider->mirrorHorizontal()); d->config.setMirrorVertical(d->resourceProvider->mirrorVertical()); d->config.setLockHorizontal(d->resourceProvider->mirrorHorizontalLock()); d->config.setLockVertical(d->resourceProvider->mirrorVerticalLock()); d->config.setHideHorizontalDecoration(d->resourceProvider->mirrorHorizontalHideDecorations()); d->config.setHideVerticalDecoration(d->resourceProvider->mirrorVerticalHideDecorations()); setVisible(d->config.mirrorHorizontal() || d->config.mirrorVertical()); emit sigConfigChanged(); } void KisMirrorAxis::setVisible(bool v) { KisCanvasDecoration::setVisible(v); KisInputManager *inputManager = view() ? view()->canvasBase()->globalInputManager() : 0; if (!inputManager) return; if (v) { inputManager->attachPriorityEventFilter(this); } else { inputManager->detachPriorityEventFilter(this); } } void KisMirrorAxis::setMirrorAxisConfig(const KisMirrorAxisConfig &config) { if (config != d->config) { KisSignalsBlocker blocker(d->resourceProvider); d->config = config; d->setAxisPosition(d->config.axisPosition().x(), d->config.axisPosition().y()); d->resourceProvider->setMirrorHorizontal(d->config.mirrorHorizontal()); d->resourceProvider->setMirrorVertical(d->config.mirrorVertical()); d->resourceProvider->setMirrorHorizontalLock(d->config.lockHorizontal()); d->resourceProvider->setMirrorVerticalLock(d->config.lockVertical()); d->resourceProvider->setMirrorHorizontal(d->config.mirrorHorizontal()); d->resourceProvider->setMirrorVertical(d->config.mirrorVertical()); d->resourceProvider->setMirrorHorizontalHideDecorations(d->config.hideHorizontalDecoration()); d->resourceProvider->setMirrorVerticalHideDecorations(d->config.hideVerticalDecoration()); } toggleMirrorActions(); setVisible(d->config.mirrorHorizontal() || d->config.mirrorVertical()); } const KisMirrorAxisConfig &KisMirrorAxis::mirrorAxisConfig() const { return d->config; } void KisMirrorAxis::toggleMirrorActions() { KActionCollection* collection = view()->viewManager()->actionCollection(); // first uncheck the action, then set according to config; // otherwise the connected KisHighlightedToolButton's highlight color is not // properly set collection->action("hmirror_action")->setChecked(false); collection->action("vmirror_action")->setChecked(false); if (d->config.mirrorHorizontal()) { collection->action("hmirror_action")->setChecked(d->config.mirrorHorizontal()); } if (d->config.mirrorVertical()) { collection->action("vmirror_action")->setChecked(d->config.mirrorVertical()); } collection->action("mirrorX-lock")->setChecked(d->config.lockHorizontal()); collection->action("mirrorY-lock")->setChecked(d->config.lockVertical()); collection->action("mirrorX-hideDecorations")->setChecked(d->config.hideHorizontalDecoration()); collection->action("mirrorY-hideDecorations")->setChecked(d->config.hideVerticalDecoration()); } void KisMirrorAxis::moveHorizontalAxisToCenter() { if (!view()->isCurrent()) { return; } d->setAxisPosition(d->image->width()/2, d->config.axisPosition().y()); emit sigConfigChanged(); } void KisMirrorAxis::moveVerticalAxisToCenter() { if (!view()->isCurrent()) { return; } d->setAxisPosition(d->config.axisPosition().x(), d->image->height()/2 ); emit sigConfigChanged(); } void KisMirrorAxis::Private::setAxisPosition(float x, float y) { QPointF newPosition = QPointF(x, y); config.setAxisPosition(newPosition); const QPointF relativePosition = KisAlgebra2D::absoluteToRelative(newPosition, image->bounds()); image->setMirrorAxesCenter(relativePosition); q->view()->canvasBase()->updateCanvas(); } void KisMirrorAxis::Private::recomputeVisibleAxes(QRect viewport) { KisCoordinatesConverter *converter = q->view()->viewConverter(); config.setAxisPosition(KisAlgebra2D::relativeToAbsolute(image->mirrorAxesCenter(), image->bounds())); QPointF samplePt1 = converter->imageToWidget(config.axisPosition()); QPointF samplePt2 = converter->imageToWidget(QPointF(config.axisPosition().x(), config.axisPosition().y() - 100)); horizontalAxis = QLineF(samplePt1, samplePt2); if (!KisAlgebra2D::intersectLineRect(horizontalAxis, viewport)) horizontalAxis = QLineF(); samplePt2 = converter->imageToWidget(QPointF(config.axisPosition().x() - 100, config.axisPosition().y())); verticalAxis = QLineF(samplePt1, samplePt2); if (!KisAlgebra2D::intersectLineRect(verticalAxis, viewport)) verticalAxis = QLineF(); } diff --git a/libs/ui/kis_selection_decoration.cc b/libs/ui/kis_selection_decoration.cc index deecb1a39d..492276fecb 100644 --- a/libs/ui/kis_selection_decoration.cc +++ b/libs/ui/kis_selection_decoration.cc @@ -1,228 +1,229 @@ /* * Copyright (c) 2008 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_decoration.h" #include #include #include #include #include "kis_types.h" #include "KisViewManager.h" #include "kis_selection.h" #include "kis_image.h" #include "flake/kis_shape_selection.h" #include "kis_pixel_selection.h" #include "kis_update_outline_job.h" #include "kis_selection_manager.h" #include "canvas/kis_canvas2.h" #include "kis_canvas_resource_provider.h" #include "kis_coordinates_converter.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_image_config.h" #include "KisImageConfigNotifier.h" #include "kis_painting_tweaks.h" #include "KisView.h" #include "kis_selection_mask.h" #include static const unsigned int ANT_LENGTH = 4; static const unsigned int ANT_SPACE = 4; static const unsigned int ANT_ADVANCE_WIDTH = ANT_LENGTH + ANT_SPACE; KisSelectionDecoration::KisSelectionDecoration(QPointerview) : KisCanvasDecoration("selection", view), m_signalCompressor(500 /*ms*/, KisSignalCompressor::FIRST_INACTIVE), m_offset(0), m_mode(Ants) { KisPaintingTweaks::initAntsPen(&m_antsPen, &m_outlinePen, ANT_LENGTH, ANT_SPACE); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); m_antsTimer = new QTimer(this); m_antsTimer->setInterval(150); m_antsTimer->setSingleShot(false); connect(m_antsTimer, SIGNAL(timeout()), SLOT(antsAttackEvent())); connect(&m_signalCompressor, SIGNAL(timeout()), SLOT(slotStartUpdateSelection())); // selections should be at the top of the stack setPriority(100); } KisSelectionDecoration::~KisSelectionDecoration() { } KisSelectionDecoration::Mode KisSelectionDecoration::mode() const { return m_mode; } void KisSelectionDecoration::setMode(Mode mode) { m_mode = mode; selectionChanged(); } bool KisSelectionDecoration::selectionIsActive() { KisImageWSP image = view()->image(); Q_ASSERT(image); Q_UNUSED(image); KisSelectionSP selection = view()->selection(); return visible() && selection && (selection->hasPixelSelection() || selection->hasShapeSelection()) && selection->isVisible(); } void KisSelectionDecoration::selectionChanged() { KisSelectionMaskSP mask = qobject_cast(view()->currentNode().data()); if (!mask || !mask->active() || !mask->visible(true)) { mask = 0; } if (!view()->isCurrent() || view()->viewManager()->mainWindow() == KisPart::instance()->currentMainwindow()) { view()->image()->setOverlaySelectionMask(mask); } KisSelectionSP selection = view()->selection(); if (!mask && selection && selectionIsActive()) { if ((m_mode == Ants && selection->outlineCacheValid()) || (m_mode == Mask && selection->thumbnailImageValid())) { m_signalCompressor.stop(); if (m_mode == Ants) { m_outlinePath = selection->outlineCache(); m_antsTimer->start(); } else { m_thumbnailImage = selection->thumbnailImage(); m_thumbnailImageTransform = selection->thumbnailImageTransform(); } if (view() && view()->canvasBase()) { view()->canvasBase()->updateCanvas(); } } else { m_signalCompressor.start(); } } else { m_signalCompressor.stop(); m_outlinePath = QPainterPath(); m_thumbnailImage = QImage(); m_thumbnailImageTransform = QTransform(); view()->canvasBase()->updateCanvas(); m_antsTimer->stop(); } } void KisSelectionDecoration::slotStartUpdateSelection() { KisSelectionSP selection = view()->selection(); if (!selection) return; view()->image()->addSpontaneousJob(new KisUpdateOutlineJob(selection, m_mode == Mask, m_maskColor)); } void KisSelectionDecoration::slotConfigChanged() { KisImageConfig imageConfig(true); KisConfig cfg(true); m_maskColor = imageConfig.selectionOverlayMaskColor(); m_antialiasSelectionOutline = cfg.antialiasSelectionOutline(); } void KisSelectionDecoration::antsAttackEvent() { KisSelectionSP selection = view()->selection(); if (!selection) return; if (selectionIsActive()) { m_offset = (m_offset + 1) % ANT_ADVANCE_WIDTH; m_antsPen.setDashOffset(m_offset); view()->canvasBase()->updateCanvas(); } } void KisSelectionDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter, KisCanvas2 *canvas) { Q_UNUSED(updateRect); Q_UNUSED(canvas); if (!selectionIsActive()) return; if ((m_mode == Ants && m_outlinePath.isEmpty()) || (m_mode == Mask && m_thumbnailImage.isNull())) return; QTransform transform = converter->imageToWidgetTransform(); gc.save(); gc.setTransform(transform, false); if (m_mode == Mask) { gc.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing, false); gc.setTransform(m_thumbnailImageTransform, true); gc.drawImage(QPoint(), m_thumbnailImage); QRect r1 = m_thumbnailImageTransform.inverted().mapRect(view()->image()->bounds()); QRect r2 = m_thumbnailImage.rect(); QPainterPath p1; p1.addRect(r1); QPainterPath p2; p2.addRect(r2); gc.setBrush(m_maskColor); gc.setPen(Qt::NoPen); gc.drawPath(p1 - p2); } else /* if (m_mode == Ants) */ { gc.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing, m_antialiasSelectionOutline); // render selection outline in white gc.setPen(m_outlinePen); gc.drawPath(m_outlinePath); // render marching ants in black (above the white outline) gc.setPen(m_antsPen); gc.drawPath(m_outlinePath); } gc.restore(); } void KisSelectionDecoration::setVisible(bool v) { KisCanvasDecoration::setVisible(v); selectionChanged(); } diff --git a/libs/ui/kis_selection_decoration.h b/libs/ui/kis_selection_decoration.h index 7c6a896818..ea90ebb475 100644 --- a/libs/ui/kis_selection_decoration.h +++ b/libs/ui/kis_selection_decoration.h @@ -1,75 +1,76 @@ /* * Copyright (c) 2008 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_SELECTION_DECORATION_H_ #define _KIS_SELECTION_DECORATION_H_ #include #include #include #include #include "canvas/kis_canvas_decoration.h" class KisView; class KRITAUI_EXPORT KisSelectionDecoration : public KisCanvasDecoration { Q_OBJECT public: KisSelectionDecoration(QPointer view); ~KisSelectionDecoration() override; enum Mode { Ants, Mask }; Mode mode() const; void setMode(Mode mode); void setVisible(bool v) override; protected: void drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas) override; private Q_SLOTS: void slotStartUpdateSelection(); void slotConfigChanged(); public Q_SLOTS: void selectionChanged(); void antsAttackEvent(); private: bool selectionIsActive(); private: KisSignalCompressor m_signalCompressor; QPainterPath m_outlinePath; QImage m_thumbnailImage; QTransform m_thumbnailImageTransform; QTimer* m_antsTimer; int m_offset; QPen m_antsPen; QPen m_outlinePen; Mode m_mode; QColor m_maskColor; bool m_antialiasSelectionOutline; }; #endif diff --git a/libs/ui/kis_tooltip_manager.cpp b/libs/ui/kis_tooltip_manager.cpp index 1bc93e3db3..c2c26678e3 100644 --- a/libs/ui/kis_tooltip_manager.cpp +++ b/libs/ui/kis_tooltip_manager.cpp @@ -1,99 +1,100 @@ /* * Copyright (c) 2014 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tooltip_manager.h" #include #include #include #include #include #include #include #include #include "KisViewManager.h" KisTooltipManager::KisTooltipManager(KisViewManager* view) : QObject(view), m_view(view), m_recording(false) { m_view->mainWindow()->menuBar()->installEventFilter(this); } KisTooltipManager::~KisTooltipManager() { if (m_recording) { QFile f("tooltips.txt"); f.open(QFile::WriteOnly); QDomDocument doc; QDomElement root; root = doc.createElement("tooltips"); doc.appendChild(root); QMapIterator it(m_tooltipMap); while (it.hasNext()) { it.next(); QDomElement tooltip = doc.createElement("tooltip"); tooltip.setAttribute("action", it.key()); tooltip.appendChild(doc.createTextNode(it.value())); root.appendChild(tooltip); } QTextStream stream(&f); stream.setCodec("UTF-8"); stream << doc.toString(); f.close(); } } void KisTooltipManager::record() { m_recording = true; QList actions = m_view->actionCollection()->actions(); Q_FOREACH (KXMLGUIClient* client, m_view->mainWindow()->childClients() ) { actions.append(client->actionCollection()->actions()); } Q_FOREACH (QAction* action, actions) { action->disconnect(); connect(action, SIGNAL(triggered()), this, SLOT(captureToolip())); } } void KisTooltipManager::captureToolip() { QString id = sender()->objectName(); QString oldTooltip; if (m_tooltipMap.contains(id)) { oldTooltip = m_tooltipMap[id]; } bool ok; QString tooltip = QInputDialog::getText(m_view->mainWindow(), "Add Tooltip", "New Tooltip:", QLineEdit::Normal, oldTooltip, &ok); if (ok && !tooltip.isEmpty()) { dynamic_cast(sender())->setToolTip(tooltip); m_tooltipMap[id] = tooltip; } } diff --git a/libs/ui/kis_tooltip_manager.h b/libs/ui/kis_tooltip_manager.h index d5a6f88064..74c3e85114 100644 --- a/libs/ui/kis_tooltip_manager.h +++ b/libs/ui/kis_tooltip_manager.h @@ -1,44 +1,45 @@ /* * Copyright (c) 2014 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISTOOLTIPMANAGER_H #define KISTOOLTIPMANAGER_H #include #include class KisViewManager; class KisTooltipManager : public QObject { Q_OBJECT public: KisTooltipManager(KisViewManager* view); ~KisTooltipManager() override; void record(); private Q_SLOTS: void captureToolip(); private: KisViewManager* m_view; bool m_recording; QMap m_tooltipMap; }; #endif // KISTOOLTIPMANAGER_H diff --git a/libs/ui/kisexiv2/kis_exif_io.cpp b/libs/ui/kisexiv2/kis_exif_io.cpp index 654f6fca0c..da86e64559 100644 --- a/libs/ui/kisexiv2/kis_exif_io.cpp +++ b/libs/ui/kisexiv2/kis_exif_io.cpp @@ -1,639 +1,640 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_exif_io.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_exiv2.h" #include #include #include #include #include struct KisExifIO::Private { }; // ---- Exception conversion functions ---- // // convert ExifVersion and FlashpixVersion to a KisMetaData value KisMetaData::Value exifVersionToKMDValue(const Exiv2::Value::AutoPtr value) { const Exiv2::DataValue* dvalue = dynamic_cast(&*value); if (dvalue) { Q_ASSERT(dvalue); QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); return KisMetaData::Value(QString(array)); } else { Q_ASSERT(value->typeId() == Exiv2::asciiString); return KisMetaData::Value(QString::fromLatin1(value->toString().c_str())); } } // convert from KisMetaData value to ExifVersion and FlashpixVersion Exiv2::Value* kmdValueToExifVersion(const KisMetaData::Value& value) { Exiv2::DataValue* dvalue = new Exiv2::DataValue; QString ver = value.asVariant().toString(); dvalue->read((const Exiv2::byte*)ver.toLatin1().constData(), ver.size()); return dvalue; } // Convert an exif array of integer string to a KisMetaData array of integer KisMetaData::Value exifArrayToKMDIntOrderedArray(const Exiv2::Value::AutoPtr value) { QList v; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); if (dvalue) { QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); for (int i = 0; i < array.size(); i++) { QChar c((char)array[i]); v.push_back(KisMetaData::Value(QString(c).toInt(0))); } } else { Q_ASSERT(value->typeId() == Exiv2::asciiString); QString str = QString::fromLatin1(value->toString().c_str()); v.push_back(KisMetaData::Value(str.toInt())); } return KisMetaData::Value(v, KisMetaData::Value::OrderedArray); } // Convert a KisMetaData array of integer to an exif array of integer string Exiv2::Value* kmdIntOrderedArrayToExifArray(const KisMetaData::Value& value) { QList v = value.asArray(); QByteArray s; for (QList::iterator it = v.begin(); it != v.end(); ++it) { int val = it->asVariant().toInt(0); s += QByteArray::number(val); } return new Exiv2::DataValue((const Exiv2::byte*)s.data(), s.size()); } QDateTime exivValueToDateTime(const Exiv2::Value::AutoPtr value) { return QDateTime::fromString(value->toString().c_str(), Qt::ISODate); } template inline T fixEndianess(T v, Exiv2::ByteOrder order) { switch (order) { case Exiv2::invalidByteOrder: return v; case Exiv2::littleEndian: return qFromLittleEndian(v); case Exiv2::bigEndian: return qFromBigEndian(v); } warnKrita << "KisExifIO: unknown byte order"; return v; } Exiv2::ByteOrder invertByteOrder(Exiv2::ByteOrder order) { switch (order) { case Exiv2::littleEndian: return Exiv2::bigEndian; case Exiv2::bigEndian: return Exiv2::littleEndian; case Exiv2::invalidByteOrder: warnKrita << "KisExifIO: Can't invert Exiv2::invalidByteOrder"; return Exiv2::invalidByteOrder; } return Exiv2::invalidByteOrder; } KisMetaData::Value exifOECFToKMDOECFStructure(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order) { QMap oecfStructure; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); Q_ASSERT(dvalue); QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); int columns = fixEndianess((reinterpret_cast(array.data()))[0], order); int rows = fixEndianess((reinterpret_cast(array.data()))[1], order); if ((columns * rows + 4) > dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera) order = invertByteOrder(order); columns = fixEndianess((reinterpret_cast(array.data()))[0], order); rows = fixEndianess((reinterpret_cast(array.data()))[1], order); Q_ASSERT((columns * rows + 4) > dvalue->count()); } oecfStructure["Columns"] = KisMetaData::Value(columns); oecfStructure["Rows"] = KisMetaData::Value(rows); int index = 4; QList names; for (int i = 0; i < columns; i++) { int lastIndex = array.indexOf((char)0, index); QString name = array.mid(index, lastIndex - index); if (index != lastIndex) { index = lastIndex + 1; dbgMetaData << "Name [" << i << "] =" << name; names.append(KisMetaData::Value(name)); } else { names.append(KisMetaData::Value("")); } } oecfStructure["Names"] = KisMetaData::Value(names, KisMetaData::Value::OrderedArray); QList values; qint32* dataIt = reinterpret_cast(array.data() + index); for (int i = 0; i < columns; i++) { for (int j = 0; j < rows; j++) { values.append(KisMetaData::Value(KisMetaData::Rational(fixEndianess(dataIt[0], order), fixEndianess(dataIt[1], order)))); dataIt += 2; } } oecfStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray); dbgMetaData << "OECF: " << ppVar(columns) << ppVar(rows) << ppVar(dvalue->count()); return KisMetaData::Value(oecfStructure); } Exiv2::Value* kmdOECFStructureToExifOECF(const KisMetaData::Value& value) { QMap oecfStructure = value.asStructure(); quint16 columns = oecfStructure["Columns"].asVariant().toInt(0); quint16 rows = oecfStructure["Rows"].asVariant().toInt(0); QList names = oecfStructure["Names"].asArray(); QList values = oecfStructure["Values"].asArray(); Q_ASSERT(columns*rows == values.size()); int length = 4 + rows * columns * 8; // The 4 byte for storing rows/columns and the rows*columns*sizeof(rational) bool saveNames = (!names.empty() && names[0].asVariant().toString().size() > 0); if (saveNames) { for (int i = 0; i < columns; i++) { length += names[i].asVariant().toString().size() + 1; } } QByteArray array(length, 0); (reinterpret_cast(array.data()))[0] = columns; (reinterpret_cast(array.data()))[1] = rows; int index = 4; if (saveNames) { for (int i = 0; i < columns; i++) { QByteArray name = names[i].asVariant().toString().toLatin1(); name.append((char)0); memcpy(array.data() + index, name.data(), name.size()); index += name.size(); } } qint32* dataIt = reinterpret_cast(array.data() + index); for (QList::iterator it = values.begin(); it != values.end(); ++it) { dataIt[0] = it->asRational().numerator; dataIt[1] = it->asRational().denominator; dataIt += 2; } return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size()); } KisMetaData::Value deviceSettingDescriptionExifToKMD(const Exiv2::Value::AutoPtr value) { QMap deviceSettingStructure; QByteArray array; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); if(dvalue) { array.resize(dvalue->count()); dvalue->copy((Exiv2::byte*)array.data()); } else { Q_ASSERT(value->typeId() == Exiv2::unsignedShort); array.resize(2 * value->count()); value->copy((Exiv2::byte*)array.data(), Exiv2::littleEndian); } int columns = (reinterpret_cast(array.data()))[0]; int rows = (reinterpret_cast(array.data()))[1]; deviceSettingStructure["Columns"] = KisMetaData::Value(columns); deviceSettingStructure["Rows"] = KisMetaData::Value(rows); QList settings; QByteArray null(2, 0); for (int index = 4; index < array.size(); ) { const int lastIndex = array.indexOf(null, index); const int numChars = (lastIndex - index) / 2; // including trailing zero QString setting = QString::fromUtf16((ushort*)(void*)( array.data() + index), numChars); index = lastIndex + 2; dbgMetaData << "Setting << " << setting; settings.append(KisMetaData::Value(setting)); } deviceSettingStructure["Settings"] = KisMetaData::Value(settings, KisMetaData::Value::OrderedArray); return KisMetaData::Value(deviceSettingStructure); } Exiv2::Value* deviceSettingDescriptionKMDToExif(const KisMetaData::Value& value) { QMap deviceSettingStructure = value.asStructure(); quint16 columns = deviceSettingStructure["Columns"].asVariant().toInt(0); quint16 rows = deviceSettingStructure["Rows"].asVariant().toInt(0); QTextCodec* codec = QTextCodec::codecForName("UTF-16"); QList settings = deviceSettingStructure["Settings"].asArray(); QByteArray array(4, 0); (reinterpret_cast(array.data()))[0] = columns; (reinterpret_cast(array.data()))[1] = rows; for (int i = 0; i < settings.count(); i++) { QString str = settings[i].asVariant().toString(); QByteArray setting = codec->fromUnicode(str); array.append(setting); } return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size()); } KisMetaData::Value cfaPatternExifToKMD(const Exiv2::Value::AutoPtr value, Exiv2::ByteOrder order) { QMap cfaPatternStructure; const Exiv2::DataValue* dvalue = dynamic_cast(&*value); Q_ASSERT(dvalue); QByteArray array(dvalue->count(), 0); dvalue->copy((Exiv2::byte*)array.data()); int columns = fixEndianess((reinterpret_cast(array.data()))[0], order); int rows = fixEndianess((reinterpret_cast(array.data()))[1], order); if ((columns * rows + 4) != dvalue->count()) { // Sometime byteOrder get messed up (especially if metadata got saved with kexiv2 library, or any library that doesn't save back with the same byte order as the camera) order = invertByteOrder(order); columns = fixEndianess((reinterpret_cast(array.data()))[0], order); rows = fixEndianess((reinterpret_cast(array.data()))[1], order); Q_ASSERT((columns * rows + 4) == dvalue->count()); } cfaPatternStructure["Columns"] = KisMetaData::Value(columns); cfaPatternStructure["Rows"] = KisMetaData::Value(rows); QList values; int index = 4; for (int i = 0; i < columns * rows; i++) { values.append(KisMetaData::Value(*(array.data() + index))); index++; } cfaPatternStructure["Values"] = KisMetaData::Value(values, KisMetaData::Value::OrderedArray); dbgMetaData << "CFAPattern " << ppVar(columns) << " " << ppVar(rows) << ppVar(values.size()) << ppVar(dvalue->count()); return KisMetaData::Value(cfaPatternStructure); } Exiv2::Value* cfaPatternKMDToExif(const KisMetaData::Value& value) { QMap cfaStructure = value.asStructure(); quint16 columns = cfaStructure["Columns"].asVariant().toInt(0); quint16 rows = cfaStructure["Rows"].asVariant().toInt(0); QList values = cfaStructure["Values"].asArray(); Q_ASSERT(columns*rows == values.size()); QByteArray array(4 + columns*rows, 0); (reinterpret_cast(array.data()))[0] = columns; (reinterpret_cast(array.data()))[1] = rows; for (int i = 0; i < columns * rows; i++) { quint8 val = values[i].asVariant().toUInt(); *(array.data() + 4 + i) = val; } dbgMetaData << "Cfa Array " << ppVar(columns) << ppVar(rows) << ppVar(array.size()); return new Exiv2::DataValue((const Exiv2::byte*)array.data(), array.size()); } // Read and write Flash // KisMetaData::Value flashExifToKMD(const Exiv2::Value::AutoPtr value) { uint16_t v = value->toLong(); QMap flashStructure; bool fired = (v & 0x01); // bit 1 is whether flash was fired or not flashStructure["Fired"] = QVariant(fired); int ret = ((v >> 1) & 0x03); // bit 2 and 3 are Return flashStructure["Return"] = QVariant(ret); int mode = ((v >> 3) & 0x03); // bit 4 and 5 are Mode flashStructure["Mode"] = QVariant(mode); bool function = ((v >> 5) & 0x01); // bit 6 if function flashStructure["Function"] = QVariant(function); bool redEye = ((v >> 6) & 0x01); // bit 7 if function flashStructure["RedEyeMode"] = QVariant(redEye); return KisMetaData::Value(flashStructure); } Exiv2::Value* flashKMDToExif(const KisMetaData::Value& value) { uint16_t v = 0; QMap flashStructure = value.asStructure(); v = flashStructure["Fired"].asVariant().toBool(); v |= ((flashStructure["Return"].asVariant().toInt() & 0x03) << 1); v |= ((flashStructure["Mode"].asVariant().toInt() & 0x03) << 3); v |= ((flashStructure["Function"].asVariant().toInt() & 0x03) << 5); v |= ((flashStructure["RedEyeMode"].asVariant().toInt() & 0x03) << 6); return new Exiv2::ValueType(v); } // ---- Implementation of KisExifIO ----// KisExifIO::KisExifIO() : d(new Private) { } KisExifIO::~KisExifIO() { delete d; } bool KisExifIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const { ioDevice->open(QIODevice::WriteOnly); Exiv2::ExifData exifData; if (headerType == KisMetaData::IOBackend::JpegHeader) { QByteArray header(6, 0); header[0] = 0x45; header[1] = 0x78; header[2] = 0x69; header[3] = 0x66; header[4] = 0x00; header[5] = 0x00; ioDevice->write(header); } for (QHash::const_iterator it = store->begin(); it != store->end(); ++it) { try { const KisMetaData::Entry& entry = *it; dbgMetaData << "Trying to save: " << entry.name() << " of " << entry.schema()->prefix() << ":" << entry.schema()->uri(); QString exivKey; if (entry.schema()->uri() == KisMetaData::Schema::TIFFSchemaUri) { exivKey = "Exif.Image." + entry.name(); } else if (entry.schema()->uri() == KisMetaData::Schema::EXIFSchemaUri) { // Distinguish between exif and gps if (entry.name().left(3) == "GPS") { exivKey = "Exif.GPS." + entry.name(); } else { exivKey = "Exif.Photo." + entry.name(); } } else if (entry.schema()->uri() == KisMetaData::Schema::DublinCoreSchemaUri) { if (entry.name() == "description") { exivKey = "Exif.Image.ImageDescription"; } else if (entry.name() == "creator") { exivKey = "Exif.Image.Artist"; } else if (entry.name() == "rights") { exivKey = "Exif.Image.Copyright"; } } else if (entry.schema()->uri() == KisMetaData::Schema::XMPSchemaUri) { if (entry.name() == "ModifyDate") { exivKey = "Exif.Image.DateTime"; } else if (entry.name() == "CreatorTool") { exivKey = "Exif.Image.Software"; } } else if (entry.schema()->uri() == KisMetaData::Schema::MakerNoteSchemaUri) { if (entry.name() == "RawData") { exivKey = "Exif.Photo.MakerNote"; } } dbgMetaData << "Saving " << entry.name() << " to " << exivKey; if (exivKey.isEmpty()) { dbgMetaData << entry.qualifiedName() << " is unsavable to EXIF"; } else { Exiv2::ExifKey exifKey(qPrintable(exivKey)); Exiv2::Value* v = 0; if (exivKey == "Exif.Photo.ExifVersion" || exivKey == "Exif.Photo.FlashpixVersion") { v = kmdValueToExifVersion(entry.value()); } else if (exivKey == "Exif.Photo.FileSource") { char s[] = { 0x03 }; v = new Exiv2::DataValue((const Exiv2::byte*)s, 1); } else if (exivKey == "Exif.Photo.SceneType") { char s[] = { 0x01 }; v = new Exiv2::DataValue((const Exiv2::byte*)s, 1); } else if (exivKey == "Exif.Photo.ComponentsConfiguration") { v = kmdIntOrderedArrayToExifArray(entry.value()); } else if (exivKey == "Exif.Image.Artist") { // load as dc:creator KisMetaData::Value creator = entry.value(); if (entry.value().asArray().size() > 0) { creator = entry.value().asArray()[0]; } #if !EXIV2_TEST_VERSION(0,21,0) v = kmdValueToExivValue(creator, Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(creator, exifKey.defaultTypeId()); #endif } else if (exivKey == "Exif.Photo.OECF") { v = kmdOECFStructureToExifOECF(entry.value()); } else if (exivKey == "Exif.Photo.DeviceSettingDescription") { v = deviceSettingDescriptionKMDToExif(entry.value()); } else if (exivKey == "Exif.Photo.CFAPattern") { v = cfaPatternKMDToExif(entry.value()); } else if (exivKey == "Exif.Photo.Flash") { v = flashKMDToExif(entry.value()); } else if (exivKey == "Exif.Photo.UserComment") { Q_ASSERT(entry.value().type() == KisMetaData::Value::LangArray); QMap langArr = entry.value().asLangArray(); if (langArr.contains("x-default")) { #if !EXIV2_TEST_VERSION(0,21,0) v = kmdValueToExivValue(langArr.value("x-default"), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(langArr.value("x-default"), exifKey.defaultTypeId()); #endif } else if (langArr.size() > 0) { #if !EXIV2_TEST_VERSION(0,21,0) v = kmdValueToExivValue(langArr.begin().value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(langArr.begin().value(), exifKey.defaultTypeId()); #endif } } else { dbgMetaData << exifKey.tag(); #if !EXIV2_TEST_VERSION(0,21,0) v = kmdValueToExivValue(entry.value(), Exiv2::ExifTags::tagType(exifKey.tag(), exifKey.ifdId())); #else v = kmdValueToExivValue(entry.value(), exifKey.defaultTypeId()); #endif } if (v && v->typeId() != Exiv2::invalidTypeId) { dbgMetaData << "Saving key" << exivKey << " of KMD value" << entry.value(); exifData.add(exifKey, v); } else { dbgMetaData << "No exif value was created for" << entry.qualifiedName() << " as" << exivKey;// << " of KMD value" << entry.value(); } } } catch (Exiv2::AnyError& e) { dbgMetaData << "exiv error " << e.what(); } } #if !EXIV2_TEST_VERSION(0,18,0) Exiv2::DataBuf rawData = exifData.copy(); ioDevice->write((const char*) rawData.pData_, rawData.size_); #else Exiv2::Blob rawData; Exiv2::ExifParser::encode(rawData, Exiv2::littleEndian, exifData); ioDevice->write((const char*) &*rawData.begin(), rawData.size()); #endif ioDevice->close(); return true; } bool KisExifIO::canSaveAllEntries(KisMetaData::Store* /*store*/) const { return false; // It's a known fact that exif can't save all information, but TODO: write the check } bool KisExifIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const { ioDevice->open(QIODevice::ReadOnly); if (!ioDevice->isOpen()) { return false; } QByteArray arr = ioDevice->readAll(); Exiv2::ExifData exifData; Exiv2::ByteOrder byteOrder; #if !EXIV2_TEST_VERSION(0,18,0) exifData.load((const Exiv2::byte*)arr.data(), arr.size()); byteOrder = exifData.byteOrder(); #else try { byteOrder = Exiv2::ExifParser::decode(exifData, (const Exiv2::byte*)arr.data(), arr.size()); } catch (const std::exception& ex) { warnKrita << "Received exception trying to parse exiv data" << ex.what(); return false; } catch (...) { dbgKrita << "Received unknown exception trying to parse exiv data"; return false; } #endif dbgMetaData << "Byte order = " << byteOrder << ppVar(Exiv2::bigEndian) << ppVar(Exiv2::littleEndian); dbgMetaData << "There are" << exifData.count() << " entries in the exif section"; const KisMetaData::Schema* tiffSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::TIFFSchemaUri); Q_ASSERT(tiffSchema); const KisMetaData::Schema* exifSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::EXIFSchemaUri); Q_ASSERT(exifSchema); const KisMetaData::Schema* dcSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::DublinCoreSchemaUri); Q_ASSERT(dcSchema); const KisMetaData::Schema* xmpSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::XMPSchemaUri); Q_ASSERT(xmpSchema); for (Exiv2::ExifMetadata::const_iterator it = exifData.begin(); it != exifData.end(); ++it) { if (it->key() == "Exif.Photo.StripOffsets" || it->key() == "RowsPerStrip" || it->key() == "StripByteCounts" || it->key() == "JPEGInterchangeFormat" || it->key() == "JPEGInterchangeFormatLength" || it->tagName() == "0x0000" ) { dbgMetaData << it->key().c_str() << " is ignored"; } else if (it->key() == "Exif.Photo.MakerNote") { const KisMetaData::Schema* makerNoteSchema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(KisMetaData::Schema::MakerNoteSchemaUri); store->addEntry(KisMetaData::Entry(makerNoteSchema, "RawData", exivValueToKMDValue(it->getValue(), false))); } else if (it->key() == "Exif.Image.DateTime") { // load as xmp:ModifyDate store->addEntry(KisMetaData::Entry(xmpSchema, "ModifyDate", KisMetaData::Value(exivValueToDateTime(it->getValue())))); } else if (it->key() == "Exif.Image.ImageDescription") { // load as "dc:description" store->addEntry(KisMetaData::Entry(dcSchema, "description", exivValueToKMDValue(it->getValue(), false))); } else if (it->key() == "Exif.Image.Software") { // load as "xmp:CreatorTool" store->addEntry(KisMetaData::Entry(xmpSchema, "CreatorTool", exivValueToKMDValue(it->getValue(), false))); } else if (it->key() == "Exif.Image.Artist") { // load as dc:creator QList creators; creators.push_back(exivValueToKMDValue(it->getValue(), false)); store->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(creators, KisMetaData::Value::OrderedArray))); } else if (it->key() == "Exif.Image.Copyright") { // load as dc:rights store->addEntry(KisMetaData::Entry(dcSchema, "rights", exivValueToKMDValue(it->getValue(), false))); } else if (it->groupName() == "Image") { // Tiff tags QString fixedTN = it->tagName().c_str(); if (it->key() == "Exif.Image.ExifTag") { dbgMetaData << "Ignoring " << it->key().c_str(); } else if (KisMetaData::Entry::isValidName(fixedTN)) { store->addEntry(KisMetaData::Entry(tiffSchema, fixedTN, exivValueToKMDValue(it->getValue(), false))) ; } else { dbgMetaData << "Invalid tag name: " << fixedTN; } } else if (it->groupName() == "Photo" || (it->groupName() == "GPS")) { // Exif tags (and GPS tags) KisMetaData::Value metaDataValue; if (it->key() == "Exif.Photo.ExifVersion" || it->key() == "Exif.Photo.FlashpixVersion") { metaDataValue = exifVersionToKMDValue(it->getValue()); } else if (it->key() == "Exif.Photo.FileSource") { metaDataValue = KisMetaData::Value(3); } else if (it->key() == "Exif.Photo.SceneType") { metaDataValue = KisMetaData::Value(1); } else if (it->key() == "Exif.Photo.ComponentsConfiguration") { metaDataValue = exifArrayToKMDIntOrderedArray(it->getValue()); } else if (it->key() == "Exif.Photo.OECF") { metaDataValue = exifOECFToKMDOECFStructure(it->getValue(), byteOrder); } else if (it->key() == "Exif.Photo.DateTimeDigitized" || it->key() == "Exif.Photo.DateTimeOriginal") { metaDataValue = KisMetaData::Value(exivValueToDateTime(it->getValue())); } else if (it->key() == "Exif.Photo.DeviceSettingDescription") { metaDataValue = deviceSettingDescriptionExifToKMD(it->getValue()); } else if (it->key() == "Exif.Photo.CFAPattern") { metaDataValue = cfaPatternExifToKMD(it->getValue(), byteOrder); } else if (it->key() == "Exif.Photo.Flash") { metaDataValue = flashExifToKMD(it->getValue()); } else if (it->key() == "Exif.Photo.UserComment") { if (it->getValue()->typeId() != Exiv2::undefined) { KisMetaData::Value vUC = exivValueToKMDValue(it->getValue(), false); Q_ASSERT(vUC.type() == KisMetaData::Value::Variant); QVariant commentVar = vUC.asVariant(); QString comment; if (commentVar.type() == QVariant::String) { comment = commentVar.toString(); } else if (commentVar.type() == QVariant::ByteArray) { const QByteArray commentString = commentVar.toByteArray(); comment = QString::fromLatin1(commentString.constData(), commentString.size()); } else { warnKrita << "KisExifIO: Unhandled UserComment value type."; } KisMetaData::Value vcomment(comment); vcomment.addPropertyQualifier("xml:lang", KisMetaData::Value("x-default")); QList alt; alt.append(vcomment); metaDataValue = KisMetaData::Value(alt, KisMetaData::Value::LangArray); } } else { bool forceSeq = false; KisMetaData::Value::ValueType arrayType = KisMetaData::Value::UnorderedArray; if (it->key() == "Exif.Photo.ISOSpeedRatings") { forceSeq = true; arrayType = KisMetaData::Value::OrderedArray; } metaDataValue = exivValueToKMDValue(it->getValue(), forceSeq, arrayType); } if (it->key() == "Exif.Photo.InteroperabilityTag" || it->key() == "Exif.Photo.0xea1d" || metaDataValue.type() == KisMetaData::Value::Invalid) { // InteroperabilityTag isn't useful for XMP, 0xea1d isn't a valid Exif tag warnMetaData << "Ignoring " << it->key().c_str(); } else { store->addEntry(KisMetaData::Entry(exifSchema, it->tagName().c_str(), metaDataValue)); } } else if (it->groupName() == "Thumbnail") { dbgMetaData << "Ignoring thumbnail tag :" << it->key().c_str(); } else { dbgMetaData << "Unknown exif tag, cannot load:" << it->key().c_str(); } } ioDevice->close(); return true; } diff --git a/libs/ui/kisexiv2/kis_exif_io.h b/libs/ui/kisexiv2/kis_exif_io.h index d9a133c82f..4a73e6973e 100644 --- a/libs/ui/kisexiv2/kis_exif_io.h +++ b/libs/ui/kisexiv2/kis_exif_io.h @@ -1,53 +1,54 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_EXIF_IO_H_ #define _KIS_EXIF_IO_H_ #include #include class KisExifIO : public KisMetaData::IOBackend { struct Private; public: KisExifIO(); ~KisExifIO() override; QString id() const override { return "exif"; } QString name() const override { return i18n("Exif"); } BackendType type() const override { return Binary; } bool supportSaving() const override { return true; } bool saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType = NoHeader) const override; bool canSaveAllEntries(KisMetaData::Store* store) const override; bool supportLoading() const override { return true; } bool loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const override; private: Private* const d; }; #endif diff --git a/libs/ui/kisexiv2/kis_xmp_io.cpp b/libs/ui/kisexiv2/kis_xmp_io.cpp index 97e0d70af3..10631c2abc 100644 --- a/libs/ui/kisexiv2/kis_xmp_io.cpp +++ b/libs/ui/kisexiv2/kis_xmp_io.cpp @@ -1,399 +1,400 @@ /* * Copyright (c) 2008-2010 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_xmp_io.h" #include #include "kis_exiv2.h" #include #include #include #include #include #include #include #include KisXMPIO::KisXMPIO() { } KisXMPIO::~KisXMPIO() { } inline std::string exiv2Prefix(const KisMetaData::Schema* _schema) { const QByteArray latin1SchemaUri = _schema->uri().toLatin1(); std::string prefix = Exiv2::XmpProperties::prefix(latin1SchemaUri.constData()); if (prefix.empty()) { dbgMetaData << "Unknown namespace " << ppVar(_schema->uri()) << ppVar(_schema->prefix()); prefix = _schema->prefix().toLatin1().constData(); Exiv2::XmpProperties::registerNs(latin1SchemaUri.constData(), prefix); } return prefix; } namespace { void saveStructure(Exiv2::XmpData& xmpData_, const QString& name, const std::string& prefix, const QMap& structure, const KisMetaData::Schema* structureSchema) { std::string structPrefix = exiv2Prefix(structureSchema); for (QMap::const_iterator it = structure.begin(); it != structure.end(); ++it) { Q_ASSERT(it.value().type() != KisMetaData::Value::Structure); // Can't nest structure QString key = QString("%1/%2:%3").arg(name).arg(structPrefix.c_str()).arg(it.key()); Exiv2::XmpKey ekey(prefix, key.toLatin1().constData()); dbgMetaData << ppVar(key) << ppVar(ekey.key().c_str()); Exiv2::Value *v = kmdValueToExivXmpValue(it.value()); if (v) { xmpData_.add(ekey, v); } } } } bool KisXMPIO::saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType) const { dbgMetaData << "Save XMP Data"; Exiv2::XmpData xmpData_; for (QHash::const_iterator it = store->begin(); it != store->end(); ++it) { const KisMetaData::Entry& entry = *it; // Check whether the prefix and namespace are know to exiv2 std::string prefix = exiv2Prefix(entry.schema()); dbgMetaData << "Saving " << entry.name(); const KisMetaData::Value& value = entry.value(); const KisMetaData::TypeInfo* typeInfo = entry.schema()->propertyType(entry.name()); if (value.type() == KisMetaData::Value::Structure) { QMap structure = value.asStructure(); const KisMetaData::Schema* structureSchema = 0; if (typeInfo) { structureSchema = typeInfo->structureSchema(); } if (!structureSchema) { dbgMetaData << "Unknown schema for " << entry.name(); structureSchema = entry.schema(); } Q_ASSERT(structureSchema); saveStructure(xmpData_, entry.name(), prefix, structure, structureSchema); } else { Exiv2::XmpKey key(prefix, entry.name().toLatin1().constData()); if (typeInfo && (typeInfo->propertyType() == KisMetaData::TypeInfo::OrderedArrayType || typeInfo->propertyType() == KisMetaData::TypeInfo::UnorderedArrayType || typeInfo->propertyType() == KisMetaData::TypeInfo::AlternativeArrayType) && typeInfo->embeddedPropertyType()->propertyType() == KisMetaData::TypeInfo::StructureType) { // Here is the bad part, again we need to do it by hand Exiv2::XmpTextValue tv; switch (typeInfo->propertyType()) { case KisMetaData::TypeInfo::OrderedArrayType: tv.setXmpArrayType(Exiv2::XmpValue::xaSeq); break; case KisMetaData::TypeInfo::UnorderedArrayType: tv.setXmpArrayType(Exiv2::XmpValue::xaBag); break; case KisMetaData::TypeInfo::AlternativeArrayType: tv.setXmpArrayType(Exiv2::XmpValue::xaAlt); break; default: // Cannot happen ; } xmpData_.add(key, &tv); // set the arrya type const KisMetaData::TypeInfo* stuctureTypeInfo = typeInfo->embeddedPropertyType(); const KisMetaData::Schema* structureSchema = 0; if (stuctureTypeInfo) { structureSchema = stuctureTypeInfo->structureSchema(); } if (!structureSchema) { dbgMetaData << "Unknown schema for " << entry.name(); structureSchema = entry.schema(); } Q_ASSERT(structureSchema); QList array = value.asArray(); for (int idx = 0; idx < array.size(); ++idx) { saveStructure(xmpData_, QString("%1[%2]").arg(entry.name()).arg(idx + 1), prefix, array[idx].asStructure(), structureSchema); } } else { dbgMetaData << ppVar(key.key().c_str()); Exiv2::Value *v = kmdValueToExivXmpValue(value); if (v) { xmpData_.add(key, v); } } } // TODO property qualifier } // Serialize data std::string xmpPacket_; Exiv2::XmpParser::encode(xmpPacket_, xmpData_); // Save data into the IO device ioDevice->open(QIODevice::WriteOnly); if (headerType == KisMetaData::IOBackend::JpegHeader) { xmpPacket_ = "http://ns.adobe.com/xap/1.0/\0" + xmpPacket_; } ioDevice->write(xmpPacket_.c_str(), xmpPacket_.length()); return true; } bool parseTagName(const QString &tagString, QString &structName, int &arrayIndex, QString &tagName, const KisMetaData::TypeInfo** typeInfo, const KisMetaData::Schema *schema) { arrayIndex = -1; *typeInfo = 0; int numSubNames = tagString.count('/') + 1; if (numSubNames == 1) { structName.clear(); tagName = tagString; *typeInfo = schema->propertyType(tagName); return true; } if (numSubNames == 2) { QRegExp regexp("([A-Za-z]\\w+)/([A-Za-z]\\w+):([A-Za-z]\\w+)"); if (regexp.indexIn(tagString) != -1) { structName = regexp.capturedTexts()[1]; tagName = regexp.capturedTexts()[3]; *typeInfo = schema->propertyType(structName); if (*typeInfo && (*typeInfo)->propertyType() == KisMetaData::TypeInfo::StructureType) { *typeInfo = (*typeInfo)->structureSchema()->propertyType(tagName); } return true; } QRegExp regexp2("([A-Za-z]\\w+)\\[(\\d+)\\]/([A-Za-z]\\w+):([A-Za-z]\\w+)"); if (regexp2.indexIn(tagString) != -1) { structName = regexp2.capturedTexts()[1]; arrayIndex = regexp2.capturedTexts()[2].toInt() - 1; tagName = regexp2.capturedTexts()[4]; if (schema->propertyType(structName)) { *typeInfo = schema->propertyType(structName)->embeddedPropertyType(); Q_ASSERT(*typeInfo); if ((*typeInfo)->propertyType() == KisMetaData::TypeInfo::StructureType) { *typeInfo = (*typeInfo)->structureSchema()->propertyType(tagName); } } return true; } } warnKrita << "WARNING: Unsupported tag. We do not yet support nested tags. The tag will be dropped!"; warnKrita << " Failing tag:" << tagString; return false; } bool KisXMPIO::loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const { ioDevice->open(QIODevice::ReadOnly); dbgMetaData << "Load XMP Data"; std::string xmpPacket_; QByteArray arr = ioDevice->readAll(); xmpPacket_.assign(arr.data(), arr.length()); dbgMetaData << xmpPacket_.length(); // dbgMetaData << xmpPacket_.c_str(); Exiv2::XmpData xmpData_; Exiv2::XmpParser::decode(xmpData_, xmpPacket_); QMap< const KisMetaData::Schema*, QMap > > structures; QMap< const KisMetaData::Schema*, QMap > > > arraysOfStructures; for (Exiv2::XmpData::iterator it = xmpData_.begin(); it != xmpData_.end(); ++it) { dbgMetaData << "Start iteration" << it->key().c_str(); Exiv2::XmpKey key(it->key()); dbgMetaData << key.groupName().c_str() << " " << key.tagName().c_str() << " " << key.ns().c_str(); if ((key.groupName() == "exif" || key.groupName() == "tiff") && key.tagName() == "NativeDigest") { // TODO: someone who has time to lose can look in adding support for NativeDigest, it's undocumented use by the XMP SDK to check if exif data has been changed while XMP hasn't been updated dbgMetaData << "dropped"; } else { const KisMetaData::Schema* schema = KisMetaData::SchemaRegistry::instance()->schemaFromPrefix(key.groupName().c_str()); if (!schema) { schema = KisMetaData::SchemaRegistry::instance()->schemaFromUri(key.ns().c_str()); if (!schema) { schema = KisMetaData::SchemaRegistry::instance()->create(key.ns().c_str(), key.groupName().c_str()); Q_ASSERT(schema); } } const Exiv2::Value::AutoPtr value = it->getValue(); QString structName; int arrayIndex = -1; QString tagName; const KisMetaData::TypeInfo* typeInfo = 0; if (!parseTagName(key.tagName().c_str(), structName, arrayIndex, tagName, &typeInfo, schema)) continue; bool isStructureEntry = !structName.isEmpty() && arrayIndex == -1; bool isStructureInArrayEntry = !structName.isEmpty() && arrayIndex != -1; Q_ASSERT(isStructureEntry != isStructureInArrayEntry || !isStructureEntry); KisMetaData::Value v; bool ignoreValue = false; // Compute the value if (value->typeId() == Exiv2::xmpBag || value->typeId() == Exiv2::xmpSeq || value->typeId() == Exiv2::xmpAlt) { const KisMetaData::TypeInfo* embeddedTypeInfo = 0; if (typeInfo) { embeddedTypeInfo = typeInfo->embeddedPropertyType(); } const KisMetaData::Parser* parser = 0; if (embeddedTypeInfo) { parser = embeddedTypeInfo->parser(); } const Exiv2::XmpArrayValue* xav = dynamic_cast(value.get()); Q_ASSERT(xav); QList array; for (int i = 0; i < xav->count(); ++i) { QString value = QString::fromStdString(xav->toString(i)); if (parser) { array.push_back(parser->parse(value)); } else { dbgImage << "No parser " << tagName; array.push_back(KisMetaData::Value(value)); } } KisMetaData::Value::ValueType vt = KisMetaData::Value::Invalid; switch (xav->xmpArrayType()) { case Exiv2::XmpValue::xaNone: warnKrita << "KisXMPIO: Unsupported array"; break; case Exiv2::XmpValue::xaAlt: vt = KisMetaData::Value::AlternativeArray; break; case Exiv2::XmpValue::xaBag: vt = KisMetaData::Value::UnorderedArray; break; case Exiv2::XmpValue::xaSeq: vt = KisMetaData::Value::OrderedArray; break; } v = KisMetaData::Value(array, vt); } else if (value->typeId() == Exiv2::langAlt) { const Exiv2::LangAltValue* xav = dynamic_cast(value.get()); QList alt; for (std::map< std::string, std::string>::const_iterator it = xav->value_.begin(); it != xav->value_.end(); ++it) { KisMetaData::Value valt(it->second.c_str()); valt.addPropertyQualifier("xml:lang", KisMetaData::Value(it->first.c_str())); alt.push_back(valt); } v = KisMetaData::Value(alt, KisMetaData::Value::LangArray); } else { QString valTxt = value->toString().c_str(); if (typeInfo && typeInfo->parser()) { v = typeInfo->parser()->parse(valTxt); } else { dbgMetaData << "No parser " << tagName; v = KisMetaData::Value(valTxt); } if (valTxt == "type=\"Struct\"") { if (!typeInfo || typeInfo->propertyType() == KisMetaData::TypeInfo::StructureType) { ignoreValue = true; } } } // set the value if (isStructureEntry) { structures[schema][structName][tagName] = v; } else if (isStructureInArrayEntry) { if (arraysOfStructures[schema][structName].size() <= arrayIndex) { arraysOfStructures[schema][structName].resize(arrayIndex + 1); } if (!arraysOfStructures[schema][structName][arrayIndex].contains(tagName)) { arraysOfStructures[schema][structName][arrayIndex][tagName] = v; } else { warnKrita << "WARNING: trying to overwrite tag" << tagName << "in" << structName << arrayIndex; } } else { if (!ignoreValue) { store->addEntry(KisMetaData::Entry(schema, tagName, v)); } else { dbgMetaData << "Ignoring value for " << tagName << " " << v; } } } } for (QMap< const KisMetaData::Schema*, QMap > >::iterator it = structures.begin(); it != structures.end(); ++it) { const KisMetaData::Schema* schema = it.key(); for (QMap >::iterator it2 = it.value().begin(); it2 != it.value().end(); ++it2) { store->addEntry(KisMetaData::Entry(schema, it2.key(), KisMetaData::Value(it2.value()))); } } for (QMap< const KisMetaData::Schema*, QMap > > >::iterator it = arraysOfStructures.begin(); it != arraysOfStructures.end(); ++it) { const KisMetaData::Schema* schema = it.key(); for (QMap > >::iterator it2 = it.value().begin(); it2 != it.value().end(); ++it2) { KisMetaData::Value::ValueType type = KisMetaData::Value::OrderedArray; QString entryName = it2.key(); if (schema->propertyType(entryName)) { switch (schema->propertyType(entryName)->propertyType()) { case KisMetaData::TypeInfo::OrderedArrayType: type = KisMetaData::Value::OrderedArray; break; case KisMetaData::TypeInfo::UnorderedArrayType: type = KisMetaData::Value::OrderedArray; break; case KisMetaData::TypeInfo::AlternativeArrayType: type = KisMetaData::Value::AlternativeArray; break; default: type = KisMetaData::Value::Invalid; break; } } else if (store->containsEntry(schema, entryName)) { KisMetaData::Value value = store->getEntry(schema, entryName).value(); if (value.isArray()) { type = value.type(); } } store->removeEntry(schema, entryName); if (type != KisMetaData::Value::Invalid) { QList< KisMetaData::Value > valueList; for (int i = 0; i < it2.value().size(); ++i) { valueList.append(it2.value()[i]); } store->addEntry(KisMetaData::Entry(schema, entryName, KisMetaData::Value(valueList, type))); } } } return true; } diff --git a/libs/ui/kisexiv2/kis_xmp_io.h b/libs/ui/kisexiv2/kis_xmp_io.h index e3d92004bc..32d95ac07b 100644 --- a/libs/ui/kisexiv2/kis_xmp_io.h +++ b/libs/ui/kisexiv2/kis_xmp_io.h @@ -1,53 +1,54 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_XMP_IO_H_ #define _KIS_XMP_IO_H_ #include #include class KisXMPIO : public KisMetaData::IOBackend { struct Private; public: KisXMPIO(); ~KisXMPIO() override; QString id() const override { return "xmp"; } QString name() const override { return i18n("XMP"); } BackendType type() const override { return Text; } bool supportSaving() const override { return true; } bool saveTo(KisMetaData::Store* store, QIODevice* ioDevice, HeaderType headerType = NoHeader) const override; bool canSaveAllEntries(KisMetaData::Store*) const override { return true; } bool supportLoading() const override { return true; } bool loadFrom(KisMetaData::Store* store, QIODevice* ioDevice) const override; }; #endif diff --git a/libs/ui/opengl/KisScreenInformationAdapter.cpp b/libs/ui/opengl/KisScreenInformationAdapter.cpp index 236d42bb92..e4c3c075c9 100644 --- a/libs/ui/opengl/KisScreenInformationAdapter.cpp +++ b/libs/ui/opengl/KisScreenInformationAdapter.cpp @@ -1,297 +1,299 @@ /* * Copyright (c) 2019 Dmitry Kazakov * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisScreenInformationAdapter.h" #include "kis_debug.h" #include #include #include #include #ifdef Q_OS_WIN #include #include #include #include #include "EGL/egl.h" #include "EGL/eglext.h" #endif namespace { struct EGLException { EGLException() {} EGLException(const QString &what) : m_what(what) {} QString what() const { return m_what; } private: QString m_what; }; template void getProcAddressSafe(QOpenGLContext *context, const char *funcName, FuncType &func) { func = reinterpret_cast(context->getProcAddress(funcName)); if (!func) { throw EGLException(QString("failed to fetch function %1").arg(funcName)); } } #ifdef Q_OS_WIN typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name); #endif } struct KisScreenInformationAdapter::Private { void initialize(QOpenGLContext *context); QOpenGLContext *context; QString errorString; #ifdef Q_OS_WIN Microsoft::WRL::ComPtr dxgiAdapter; #endif }; KisScreenInformationAdapter::KisScreenInformationAdapter(QOpenGLContext *context) : m_d(new Private) { - m_d->initialize(context); + if (context) { + m_d->initialize(context); + } } KisScreenInformationAdapter::~KisScreenInformationAdapter() { } void KisScreenInformationAdapter::Private::initialize(QOpenGLContext *newContext) { context = newContext; errorString.clear(); try { #if defined Q_OS_WIN && QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) if (!context->isOpenGLES()) { throw EGLException("the context is not OpenGL ES"); } PFNEGLQUERYSTRINGPROC queryString = nullptr; getProcAddressSafe(context, "eglQueryString", queryString); const char* client_extensions = queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); const QList extensions = QByteArray(client_extensions).split(' '); if (!extensions.contains("EGL_ANGLE_platform_angle_d3d") || !extensions.contains("EGL_ANGLE_device_creation_d3d11")) { throw EGLException("the context is not Angle + D3D11"); } PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttribEXT = nullptr; PFNEGLQUERYDEVICEATTRIBEXTPROC queryDeviceAttribEXT = nullptr; getProcAddressSafe(context, "eglQueryDisplayAttribEXT", queryDisplayAttribEXT); getProcAddressSafe(context, "eglQueryDeviceAttribEXT", queryDeviceAttribEXT); QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); EGLDisplay display = reinterpret_cast(nativeInterface->nativeResourceForContext("egldisplay", context)); if (!display) { throw EGLException( QString("couldn't request EGLDisplay handle, display = 0x%1").arg(uintptr_t(display), 0, 16)); } EGLAttrib value = 0; EGLBoolean result = false; result = queryDisplayAttribEXT(display, EGL_DEVICE_EXT, &value); if (!result || value == EGL_NONE) { throw EGLException( QString("couldn't request EGLDeviceEXT handle, result = 0x%1, value = 0x%2") .arg(result, 0, 16).arg(value, 0, 16)); } EGLDeviceEXT device = reinterpret_cast(value); result = queryDeviceAttribEXT(device, EGL_D3D11_DEVICE_ANGLE, &value); if (!result || value == EGL_NONE) { throw EGLException( QString("couldn't request ID3D11Device pointer, result = 0x%1, value = 0x%2") .arg(result, 0, 16).arg(value, 0, 16)); } ID3D11Device *deviceD3D = reinterpret_cast(value); { HRESULT result = 0; Microsoft::WRL::ComPtr dxgiDevice; result = deviceD3D->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); if (FAILED(result)) { throw EGLException( QString("couldn't request IDXGIDevice pointer, result = 0x%1").arg(result, 0, 16)); } Microsoft::WRL::ComPtr dxgiAdapter; result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter1), (void**)&dxgiAdapter); if (FAILED(result)) { throw EGLException( QString("couldn't request IDXGIAdapter1 pointer, result = 0x%1").arg(result, 0, 16)); } this->dxgiAdapter = dxgiAdapter; } #else throw EGLException("current platform doesn't support fetching display information"); #endif } catch (EGLException &e) { this->context = 0; this->errorString = e.what(); #ifdef Q_OS_WIN this->dxgiAdapter.Reset(); #endif } } bool KisScreenInformationAdapter::isValid() const { #ifdef Q_OS_WIN return m_d->context && m_d->dxgiAdapter; #else return false; #endif } QString KisScreenInformationAdapter::errorString() const { return m_d->errorString; } KisScreenInformationAdapter::ScreenInfo KisScreenInformationAdapter::infoForScreen(QScreen *screen) const { ScreenInfo info; #if defined Q_OS_WIN && QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); HMONITOR monitor = reinterpret_cast(nativeInterface->nativeResourceForScreen("handle", screen)); if (!monitor) { qWarning("%s: failed to get HMONITOR handle for screen: screen = 0x%X, monitor = 0x%X", __PRETTY_FUNCTION__, screen, monitor); } UINT i = 0; Microsoft::WRL::ComPtr currentOutput; while (m_d->dxgiAdapter->EnumOutputs(i, ¤tOutput) != DXGI_ERROR_NOT_FOUND) { HRESULT result = 0; Microsoft::WRL::ComPtr output6; result = currentOutput.As(&output6); if (output6) { DXGI_OUTPUT_DESC1 desc; result = output6->GetDesc1(&desc); if (desc.Monitor == monitor) { info.screen = screen; info.bitsPerColor = desc.BitsPerColor; info.redPrimary[0] = desc.RedPrimary[0]; info.redPrimary[1] = desc.RedPrimary[1]; info.greenPrimary[0] = desc.GreenPrimary[0]; info.greenPrimary[1] = desc.GreenPrimary[1]; info.bluePrimary[0] = desc.BluePrimary[0]; info.bluePrimary[1] = desc.BluePrimary[1]; info.whitePoint[0] = desc.WhitePoint[0]; info.whitePoint[1] = desc.WhitePoint[1]; info.minLuminance = desc.MinLuminance; info.maxLuminance = desc.MaxLuminance; info.maxFullFrameLuminance = desc.MaxFullFrameLuminance; info.colorSpace = KisSurfaceColorSpace::DefaultColorSpace; if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { info.colorSpace = KisSurfaceColorSpace::sRGBColorSpace; } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { #ifdef HAVE_HDR info.colorSpace = KisSurfaceColorSpace::scRGBColorSpace; #else qWarning("WARNING: scRGB display color space is not supported by Qt's build"); #endif } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { #ifdef HAVE_HDR info.colorSpace = KisSurfaceColorSpace::bt2020PQColorSpace; #else qWarning("WARNING: bt2020-pq display color space is not supported by Qt's build"); #endif } else { qWarning("WARNING: unknown display color space! 0x%X", desc.ColorSpace); } break; } } i++; } #endif Q_UNUSED(screen); return info; } QDebug operator<<(QDebug dbg, const KisScreenInformationAdapter::ScreenInfo &info) { QDebugStateSaver saver(dbg); if (info.isValid()) { dbg.nospace() << "ScreenInfo(" << "screen " << info.screen << ", bitsPerColor " << info.bitsPerColor << ", colorSpace " << info.colorSpace << ", redPrimary " << "(" << info.redPrimary[0] << ", " << info.redPrimary[1] << ")" << ", greenPrimary " << "(" << info.greenPrimary[0] << ", " << info.greenPrimary[1] << ")" << ", bluePrimary " << "(" << info.bluePrimary[0] << ", " << info.bluePrimary[1] << ")" << ", whitePoint " << "(" << info.whitePoint[0] << ", " << info.whitePoint[1] << ")" << ", minLuminance " << info.minLuminance << ", maxLuminance " << info.maxLuminance << ", maxFullFrameLuminance " << info.maxFullFrameLuminance << ')'; } else { dbg.nospace() << "ScreenInfo()"; } return dbg; } diff --git a/libs/ui/widgets/KisGamutMaskToolbar.cpp b/libs/ui/widgets/KisGamutMaskToolbar.cpp index 9ce0bd6c4e..d250c50460 100644 --- a/libs/ui/widgets/KisGamutMaskToolbar.cpp +++ b/libs/ui/widgets/KisGamutMaskToolbar.cpp @@ -1,115 +1,116 @@ /* * Copyright (c) 2018 Anna Medonosova * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "KisGamutMaskToolbar.h" #include #include KisGamutMaskToolbar::KisGamutMaskToolbar(QWidget* parent) : QWidget(parent) , m_selectedMask(nullptr) { m_ui = new Ui_wdgGamutMaskToolbar(); m_ui->setupUi(this); m_iconMaskOff = KisIconUtils::loadIcon("gamut-mask-off"); m_iconMaskOn = KisIconUtils::loadIcon("gamut-mask-on"); m_textNoMask = i18n("Select a mask in \"Gamut Masks\" docker"); m_textMaskDisabled = i18n("Mask is disabled"); m_ui->bnToggleMask->setChecked(false); m_ui->bnToggleMask->setIcon(m_iconMaskOff); m_ui->rotationSlider->setRange(0, 360); m_ui->rotationSlider->setPrefix(i18n("Rotation: ")); m_ui->rotationSlider->setSuffix("°"); m_ui->rotationSlider->setFastSliderStep(30); // TODO: test for usability m_ui->rotationSlider->hide(); // gamut mask connections connect(m_ui->bnToggleMask, SIGNAL(toggled(bool)), SLOT(slotGamutMaskToggle(bool))); connect(m_ui->rotationSlider, SIGNAL(valueChanged(int)), SLOT(slotGamutMaskRotate(int))); } void KisGamutMaskToolbar::connectMaskSignals(KisCanvasResourceProvider* resourceProvider) { connect(resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)), this, SLOT(slotGamutMaskSet(KoGamutMaskSP)), Qt::UniqueConnection); connect(resourceProvider, SIGNAL(sigGamutMaskUnset()), this, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection); connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)), resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), Qt::UniqueConnection); } void KisGamutMaskToolbar::slotGamutMaskSet(KoGamutMaskSP mask) { if (!mask) { return; } m_selectedMask = mask; if (m_selectedMask) { slotGamutMaskToggle(true); } else { slotGamutMaskToggle(false); } } void KisGamutMaskToolbar::slotGamutMaskUnset() { m_ui->rotationSlider->hide(); m_ui->labelMaskName->show(); m_ui->labelMaskName->setText(m_textNoMask); } void KisGamutMaskToolbar::slotGamutMaskToggle(bool state) { bool b = (!m_selectedMask) ? false : state; m_ui->bnToggleMask->setChecked(b); if (b == true) { m_ui->bnToggleMask->setIcon(m_iconMaskOn); m_ui->labelMaskName->hide(); m_ui->rotationSlider->show(); m_ui->rotationSlider->blockSignals(true); m_ui->rotationSlider->setValue(m_selectedMask->rotation()); m_ui->rotationSlider->blockSignals(false); } else { m_ui->bnToggleMask->setIcon(m_iconMaskOff); m_ui->rotationSlider->hide(); m_ui->labelMaskName->show(); m_ui->labelMaskName->setText(m_textMaskDisabled); } emit sigGamutMaskToggle(state); } void KisGamutMaskToolbar::slotGamutMaskRotate(int angle) { if (!m_selectedMask) { return; } m_selectedMask->setRotation(angle); emit sigGamutMaskChanged(m_selectedMask); } diff --git a/libs/ui/widgets/KisGamutMaskToolbar.h b/libs/ui/widgets/KisGamutMaskToolbar.h index da08ab5817..a99182b5f9 100644 --- a/libs/ui/widgets/KisGamutMaskToolbar.h +++ b/libs/ui/widgets/KisGamutMaskToolbar.h @@ -1,61 +1,62 @@ /* * Copyright (c) 2018 Anna Medonosova * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISGAMUTMASKTOOLBAR_H #define KISGAMUTMASKTOOLBAR_H #include #include #include #include "kritaui_export.h" #include "ui_wdgGamutMaskToolbar.h" class KisCanvasResourceProvider; class KRITAUI_EXPORT KisGamutMaskToolbar : public QWidget { Q_OBJECT public: KisGamutMaskToolbar(QWidget* parent = nullptr); void connectMaskSignals(KisCanvasResourceProvider* resourceProvider); Q_SIGNALS: void sigGamutMaskToggle(bool state); void sigGamutMaskChanged(KoGamutMaskSP); public Q_SLOTS: void slotGamutMaskSet(KoGamutMaskSP mask); void slotGamutMaskUnset(); private Q_SLOTS: void slotGamutMaskToggle(bool state); void slotGamutMaskRotate(int angle); private: Ui_wdgGamutMaskToolbar* m_ui; KoGamutMaskSP m_selectedMask; QIcon m_iconMaskOff; QIcon m_iconMaskOn; QString m_textNoMask; QString m_textMaskDisabled; }; #endif // KISGAMUTMASKTOOLBAR_H diff --git a/libs/ui/widgets/KisLayerStyleAngleSelector.cpp b/libs/ui/widgets/KisLayerStyleAngleSelector.cpp index e576080b0a..87680a0203 100644 --- a/libs/ui/widgets/KisLayerStyleAngleSelector.cpp +++ b/libs/ui/widgets/KisLayerStyleAngleSelector.cpp @@ -1,117 +1,118 @@ /* * Copyright (c) 2018 Anna Medonosova * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisLayerStyleAngleSelector.h" #include #include #include KisLayerStyleAngleSelector::KisLayerStyleAngleSelector(QWidget *parent) : QWidget(parent) , m_enableGlobalLight(false) { ui = new Ui_WdgKisLayerStyleAngleSelector(); ui->setupUi(this); ui->chkUseGlobalLight->hide(); connect(ui->dialAngle, SIGNAL(valueChanged(int)), SLOT(slotDialAngleChanged(int))); connect(ui->intAngle, SIGNAL(valueChanged(int)), SLOT(slotIntAngleChanged(int))); } int KisLayerStyleAngleSelector::value() { return ui->intAngle->value(); } void KisLayerStyleAngleSelector::setValue(int value) { KisSignalsBlocker intB(ui->intAngle); KisSignalsBlocker dialB(ui->dialAngle); ui->intAngle->setValue(value); ui->dialAngle->setValue(value + m_dialValueShift); } void KisLayerStyleAngleSelector::enableGlobalLight(bool enable) { m_enableGlobalLight = enable; if (enable) { ui->chkUseGlobalLight->show(); connect(ui->chkUseGlobalLight, SIGNAL(toggled(bool)), SLOT(slotGlobalLightToggled())); } else { ui->chkUseGlobalLight->hide(); disconnect(ui->chkUseGlobalLight, SIGNAL(toggled(bool)), this, SLOT(slotGlobalLightToggled())); } } bool KisLayerStyleAngleSelector::useGlobalLight() { return m_enableGlobalLight && ui->chkUseGlobalLight->isChecked(); } void KisLayerStyleAngleSelector::setUseGlobalLight(bool state) { ui->chkUseGlobalLight->setChecked(state); } void KisLayerStyleAngleSelector::slotDialAngleChanged(int value) { KisSignalsBlocker b(ui->intAngle); int normalizedValue = 0; if (value >= 270 && value <= 360) { // Due to the mismatch between the domain of the dial (0°,360°) // and the spinbox (-179°,180°), the shift in the third quadrant // of the dial is different normalizedValue = value - 360 - m_dialValueShift; } else { normalizedValue = value - m_dialValueShift; } ui->intAngle->setValue(normalizedValue); emit valueChanged(normalizedValue); emitChangeSignals(); } void KisLayerStyleAngleSelector::slotIntAngleChanged(int value) { KisSignalsBlocker b(ui->dialAngle); int angleDialValue = value + m_dialValueShift; ui->dialAngle->setValue(angleDialValue); emit valueChanged(value); emitChangeSignals(); } void KisLayerStyleAngleSelector::slotGlobalLightToggled() { emitChangeSignals(); } void KisLayerStyleAngleSelector::emitChangeSignals() { if (useGlobalLight()) { emit globalAngleChanged(value()); } emit configChanged(); } diff --git a/libs/ui/widgets/KisLayerStyleAngleSelector.h b/libs/ui/widgets/KisLayerStyleAngleSelector.h index 35eb382641..970078beb0 100644 --- a/libs/ui/widgets/KisLayerStyleAngleSelector.h +++ b/libs/ui/widgets/KisLayerStyleAngleSelector.h @@ -1,68 +1,69 @@ /* * Copyright (c) 2018 Anna Medonosova * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISLAYERSTYLEANGLESELECTOR_H #define KISLAYERSTYLEANGLESELECTOR_H #include #include #include #include class KisLayerStyleAngleSelector : public QWidget { Q_OBJECT public: KisLayerStyleAngleSelector(QWidget* parent); int value(); void setValue(int value); void enableGlobalLight(bool enable); bool useGlobalLight(); void setUseGlobalLight(bool state); Q_SIGNALS: void valueChanged(int); void configChanged(); void globalAngleChanged(int); private Q_SLOTS: void slotDialAngleChanged(int value); void slotIntAngleChanged(int value); void slotGlobalLightToggled(); private: void emitChangeSignals(); Ui_WdgKisLayerStyleAngleSelector* ui; // BUG: 372169 // Adobe's dial widget differs from QDial by 90 degrees, // therefore we need to apply this magic constant // to this widget's QDial for consistency between // the settings dialogs and on-canvas effects. // Wrapping is handled by QDial itself. const static int m_dialValueShift = 90; bool m_enableGlobalLight; }; #endif // KISLAYERSTYLEANGLESELECTOR_H diff --git a/libs/widgets/kis_color_input.cpp b/libs/widgets/kis_color_input.cpp index 0ff533e202..4bbd8e02f3 100644 --- a/libs/widgets/kis_color_input.cpp +++ b/libs/widgets/kis_color_input.cpp @@ -1,416 +1,417 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2011 Sven Langkamp * Copyright (c) 2015 Moritz Molch * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_input.h" #include #ifdef HAVE_OPENEXR #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_double_parse_spin_box.h" #include "kis_int_parse_spin_box.h" KisColorInput::KisColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : QWidget(parent), m_channelInfo(channelInfo), m_color(color), m_displayRenderer(displayRenderer), m_usePercentage(usePercentage) { } void KisColorInput::init() { QHBoxLayout* m_layout = new QHBoxLayout(this); m_layout->setContentsMargins(0,0,0,0); m_layout->setSpacing(1); QLabel* m_label = new QLabel(i18n("%1:", m_channelInfo->name()), this); m_layout->addWidget(m_label); m_colorSlider = new KoColorSlider(Qt::Horizontal, this, m_displayRenderer); m_colorSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_layout->addWidget(m_colorSlider); QWidget* m_input = createInput(); m_colorSlider->setFixedHeight(m_input->sizeHint().height()); m_layout->addWidget(m_input); } KisIntegerColorInput::KisIntegerColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage) { init(); } void KisIntegerColorInput::setValue(int v) { quint8* data = m_color->data() + m_channelInfo->pos(); switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: *(reinterpret_cast(data)) = v; break; case KoChannelInfo::UINT16: *(reinterpret_cast(data)) = v; break; case KoChannelInfo::UINT32: *(reinterpret_cast(data)) = v; break; default: Q_ASSERT(false); } emit(updated()); } void KisIntegerColorInput::update() { KoColor min = *m_color; KoColor max = *m_color; quint8* data = m_color->data() + m_channelInfo->pos(); quint8* dataMin = min.data() + m_channelInfo->pos(); quint8* dataMax = max.data() + m_channelInfo->pos(); m_intNumInput->blockSignals(true); m_colorSlider->blockSignals(true); switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: if (m_usePercentage) { m_intNumInput->setMaximum(100); m_intNumInput->setValue(round(*(reinterpret_cast(data))*1.0 / 255.0 * 100.0)); } else { m_intNumInput->setMaximum(0xFF); m_intNumInput->setValue(*(reinterpret_cast(data))); } m_colorSlider->setValue(*(reinterpret_cast(data))); *(reinterpret_cast(dataMin)) = 0x0; *(reinterpret_cast(dataMax)) = 0xFF; break; case KoChannelInfo::UINT16: if (m_usePercentage) { m_intNumInput->setMaximum(100); m_intNumInput->setValue(round(*(reinterpret_cast(data))*1.0 / 65535.0 * 100.0)); } else { m_intNumInput->setMaximum(0xFFFF); m_intNumInput->setValue(*(reinterpret_cast(data))); } m_colorSlider->setValue(*(reinterpret_cast(data))); *(reinterpret_cast(dataMin)) = 0x0; *(reinterpret_cast(dataMax)) = 0xFFFF; break; case KoChannelInfo::UINT32: if (m_usePercentage) { m_intNumInput->setMaximum(100); m_intNumInput->setValue(round(*(reinterpret_cast(data))*1.0 / 4294967295.0 * 100.0)); } else { m_intNumInput->setMaximum(0xFFFF); m_intNumInput->setValue(*(reinterpret_cast(data))); } m_colorSlider->setValue(*(reinterpret_cast(data))); *(reinterpret_cast(dataMin)) = 0x0; *(reinterpret_cast(dataMax)) = 0xFFFFFFFF; break; default: Q_ASSERT(false); } m_colorSlider->setColors(min, max); m_intNumInput->blockSignals(false); m_colorSlider->blockSignals(false); } QWidget* KisIntegerColorInput::createInput() { m_intNumInput = new KisIntParseSpinBox(this); m_intNumInput->setMinimum(0); m_colorSlider->setMinimum(0); if (m_usePercentage) { m_intNumInput->setSuffix("%"); } else { m_intNumInput->setSuffix(""); } switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: if (m_usePercentage) { m_intNumInput->setMaximum(100); } else { m_intNumInput->setMaximum(0xFF); } m_colorSlider->setMaximum(0xFF); break; case KoChannelInfo::UINT16: if (m_usePercentage) { m_intNumInput->setMaximum(100); } else { m_intNumInput->setMaximum(0xFFFF); } m_colorSlider->setMaximum(0xFFFF); break; case KoChannelInfo::UINT32: if (m_usePercentage) { m_intNumInput->setMaximum(100); } else { m_intNumInput->setMaximum(0xFFFFFFFF); } m_colorSlider->setMaximum(0xFFFFFFFF); break; default: Q_ASSERT(false); } connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(onColorSliderChanged(int))); connect(m_intNumInput, SIGNAL(valueChanged(int)), this, SLOT(onNumInputChanged(int))); return m_intNumInput; } void KisIntegerColorInput::setPercentageWise(bool val) { m_usePercentage = val; if (m_usePercentage) { m_intNumInput->setSuffix("%"); } else { m_intNumInput->setSuffix(""); } } void KisIntegerColorInput::onColorSliderChanged(int val) { m_intNumInput->blockSignals(true); if (m_usePercentage) { switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: m_intNumInput->setValue(round((val*1.0) / 255.0 * 100.0)); break; case KoChannelInfo::UINT16: m_intNumInput->setValue(round((val*1.0) / 65535.0 * 100.0)); break; case KoChannelInfo::UINT32: m_intNumInput->setValue(round((val*1.0) / 4294967295.0 * 100.0)); break; default: Q_ASSERT(false); } } else { m_intNumInput->setValue(val); } m_intNumInput->blockSignals(false); setValue(val); } void KisIntegerColorInput::onNumInputChanged(int val) { m_colorSlider->blockSignals(true); if (m_usePercentage) { switch (m_channelInfo->channelValueType()) { case KoChannelInfo::UINT8: m_colorSlider->setValue((val*1.0)/100.0 * 255.0); m_colorSlider->blockSignals(false); setValue((val*1.0)/100.0 * 255.0); break; case KoChannelInfo::UINT16: m_colorSlider->setValue((val*1.0)/100.0 * 65535.0); m_colorSlider->blockSignals(false); setValue((val*1.0)/100.0 * 65535.0); break; case KoChannelInfo::UINT32: m_colorSlider->setValue((val*1.0)/100.0 * 4294967295.0); m_colorSlider->blockSignals(false); setValue((val*1.0)/100.0 * 4294967295.0); break; default: Q_ASSERT(false); } } else { m_colorSlider->setValue(val); m_colorSlider->blockSignals(false); setValue(val); } } KisFloatColorInput::KisFloatColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage) { init(); } void KisFloatColorInput::setValue(double v) { quint8* data = m_color->data() + m_channelInfo->pos(); switch (m_channelInfo->channelValueType()) { #ifdef HAVE_OPENEXR case KoChannelInfo::FLOAT16: *(reinterpret_cast(data)) = v; break; #endif case KoChannelInfo::FLOAT32: *(reinterpret_cast(data)) = v; break; default: Q_ASSERT(false); } emit(updated()); } QWidget* KisFloatColorInput::createInput() { m_dblNumInput = new KisDoubleParseSpinBox(this); m_dblNumInput->setMinimum(0); m_dblNumInput->setMaximum(1.0); connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); connect(m_dblNumInput, SIGNAL(valueChanged(double)), this, SLOT(setValue(double))); m_dblNumInput->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); m_dblNumInput->setMinimumWidth(60); m_dblNumInput->setMaximumWidth(60); return m_dblNumInput; } void KisFloatColorInput::sliderChanged(int i) { const qreal floatRange = m_maxValue - m_minValue; m_dblNumInput->setValue(m_minValue + (i / 255.0) * floatRange); } void KisFloatColorInput::update() { KoColor min = *m_color; KoColor max = *m_color; quint8* data = m_color->data() + m_channelInfo->pos(); quint8* dataMin = min.data() + m_channelInfo->pos(); quint8* dataMax = max.data() + m_channelInfo->pos(); qreal value = 1.0; m_minValue = m_displayRenderer->minVisibleFloatValue(m_channelInfo); m_maxValue = m_displayRenderer->maxVisibleFloatValue(m_channelInfo); switch (m_channelInfo->channelValueType()) { #ifdef HAVE_OPENEXR case KoChannelInfo::FLOAT16: value = *(reinterpret_cast(data)); m_minValue = qMin(value, m_minValue); m_maxValue = qMax(value, m_maxValue); *(reinterpret_cast(dataMin)) = m_minValue; *(reinterpret_cast(dataMax)) = m_maxValue; break; #endif case KoChannelInfo::FLOAT32: value = *(reinterpret_cast(data)); m_minValue = qMin(value, m_minValue); m_maxValue = qMax(value, m_maxValue); *(reinterpret_cast(dataMin)) = m_minValue; *(reinterpret_cast(dataMax)) = m_maxValue; break; default: Q_ASSERT(false); } m_dblNumInput->setMinimum(m_minValue); m_dblNumInput->setMaximum(m_maxValue); // ensure at least 3 significant digits are always shown int newPrecision = 2 + qMax(qreal(0.0), std::ceil(-std::log10(m_maxValue))); if (newPrecision != m_dblNumInput->decimals()) { m_dblNumInput->setDecimals(newPrecision); m_dblNumInput->updateGeometry(); } m_colorSlider->setColors(min, max); const qreal floatRange = m_maxValue - m_minValue; m_dblNumInput->setValue(value); m_colorSlider->setValue((value - m_minValue) / floatRange * 255); } KisHexColorInput::KisHexColorInput(QWidget* parent, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) : KisColorInput(parent, 0, color, displayRenderer, usePercentage) { QHBoxLayout* m_layout = new QHBoxLayout(this); m_layout->setContentsMargins(0,0,0,0); m_layout->setSpacing(1); QLabel* m_label = new QLabel(i18n("Color name:"), this); m_label->setMinimumWidth(50); m_layout->addWidget(m_label); QWidget* m_input = createInput(); m_input->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); m_layout->addWidget(m_input); } void KisHexColorInput::setValue() { QString valueString = m_hexInput->text(); valueString.remove(QChar('#')); QList channels = m_color->colorSpace()->channels(); channels = KoChannelInfo::displayOrderSorted(channels); Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR) { Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8); quint8* data = m_color->data() + channel->pos(); int value = valueString.left(2).toInt(0, 16); *(reinterpret_cast(data)) = value; valueString.remove(0, 2); } } emit(updated()); } void KisHexColorInput::update() { QString hexString("#"); QList channels = m_color->colorSpace()->channels(); channels = KoChannelInfo::displayOrderSorted(channels); Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR) { Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8); quint8* data = m_color->data() + channel->pos(); hexString.append(QString("%1").arg(*(reinterpret_cast(data)), 2, 16, QChar('0'))); } } m_hexInput->setText(hexString); } QWidget* KisHexColorInput::createInput() { m_hexInput = new QLineEdit(this); m_hexInput->setAlignment(Qt::AlignRight); int digits = 2*m_color->colorSpace()->colorChannelCount(); QString pattern = QString("#?[a-fA-F0-9]{%1,%2}").arg(digits).arg(digits); m_hexInput->setValidator(new QRegExpValidator(QRegExp(pattern), this)); connect(m_hexInput, SIGNAL(editingFinished()), this, SLOT(setValue())); return m_hexInput; } diff --git a/libs/widgets/kis_color_input.h b/libs/widgets/kis_color_input.h index 51d910be68..0a3ffcb276 100644 --- a/libs/widgets/kis_color_input.h +++ b/libs/widgets/kis_color_input.h @@ -1,111 +1,112 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_COLOR_INPUT_H_ #define _KIS_COLOR_INPUT_H_ #include class KoChannelInfo; class KoColor; class QWidget; class QSpinBox; class QDoubleSpinBox; class KisIntParseSpinBox; class KisDoubleParseSpinBox; class KoColorSlider; class QLineEdit; #include #include "kritawidgets_export.h" class KRITAWIDGETS_EXPORT KisColorInput : public QWidget { Q_OBJECT public: KisColorInput(QWidget* parent, const KoChannelInfo*, KoColor* color, KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), bool usePercentage = false); inline bool usePercentage() const { return m_usePercentage; } virtual inline void setPercentageWise(bool val) { m_usePercentage = val; } protected: void init(); virtual QWidget* createInput() = 0; Q_SIGNALS: void updated(); protected: const KoChannelInfo* m_channelInfo; KoColor* m_color; KoColorSlider* m_colorSlider; KoColorDisplayRendererInterface *m_displayRenderer; bool m_usePercentage; }; class KRITAWIDGETS_EXPORT KisIntegerColorInput : public KisColorInput { Q_OBJECT public: KisIntegerColorInput(QWidget* parent, const KoChannelInfo*, KoColor* color, KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), bool usePercentage = false); protected: QWidget* createInput() override; void setPercentageWise(bool val) override; public Q_SLOTS: void setValue(int); void update(); private Q_SLOTS: void onColorSliderChanged(int); void onNumInputChanged(int); private: KisIntParseSpinBox* m_intNumInput; }; class KRITAWIDGETS_EXPORT KisFloatColorInput : public KisColorInput { Q_OBJECT public: KisFloatColorInput(QWidget* parent, const KoChannelInfo*, KoColor* color, KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), bool usePercentage = false); protected: QWidget* createInput() override; public Q_SLOTS: void setValue(double); void sliderChanged(int); void update(); private: KisDoubleParseSpinBox* m_dblNumInput; qreal m_minValue; qreal m_maxValue; }; class KRITAWIDGETS_EXPORT KisHexColorInput : public KisColorInput { Q_OBJECT public: KisHexColorInput(QWidget* parent, KoColor* color, KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), bool usePercentage = false); protected: QWidget* createInput() override; public Q_SLOTS: void setValue(); void update(); private: QLineEdit* m_hexInput; }; #endif diff --git a/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc b/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc index bf84366def..3fd53bd449 100644 --- a/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc +++ b/plugins/assistants/Assistants/ConcentricEllipseAssistant.cc @@ -1,207 +1,208 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ConcentricEllipseAssistant.h" #include #include "kis_debug.h" #include #include #include #include #include #include #include ConcentricEllipseAssistant::ConcentricEllipseAssistant() : KisPaintingAssistant("concentric ellipse", i18n("Concentric Ellipse assistant")) { } KisPaintingAssistantSP ConcentricEllipseAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new ConcentricEllipseAssistant(*this, handleMap)); } ConcentricEllipseAssistant::ConcentricEllipseAssistant(const ConcentricEllipseAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , m_ellipse(rhs.m_ellipse) , m_extraEllipse(rhs.m_extraEllipse) { } QPointF ConcentricEllipseAssistant::project(const QPointF& pt, const QPointF& strokeBegin) const { Q_ASSERT(isAssistantComplete()); m_ellipse.set(*handles()[0], *handles()[1], *handles()[2]); qreal dx = pt.x() - strokeBegin.x(), dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //calculate ratio QPointF initial = m_ellipse.project(strokeBegin); QPointF center = m_ellipse.boundingRect().center(); qreal Ratio = QLineF(center, strokeBegin).length() /QLineF(center, initial).length(); //calculate the points of the extrapolated ellipse. QLineF extrapolate0 = QLineF(center, *handles()[0]); extrapolate0.setLength(extrapolate0.length()*Ratio); QLineF extrapolate1 = QLineF(center, *handles()[1]); extrapolate1.setLength(extrapolate1.length()*Ratio); QLineF extrapolate2 = QLineF(center, *handles()[2]); extrapolate2.setLength(extrapolate2.length()*Ratio); //set the extrapolation ellipse. m_extraEllipse.set(extrapolate0.p2(), extrapolate1.p2(), extrapolate2.p2()); return m_extraEllipse.project(pt); } QPointF ConcentricEllipseAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin) { return project(pt, strokeBegin); } void ConcentricEllipseAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPointF mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in the ellipse assistant, you may have passed arguments incorrectly:"<documentToWidgetTransform(); if (isSnappingActive() && previewVisible == true){ if (isAssistantComplete()){ if (m_ellipse.set(*handles()[0], *handles()[1], *handles()[2])) { QPointF initial = m_ellipse.project(initialTransform.inverted().map(mousePos)); QPointF center = m_ellipse.boundingRect().center(); qreal Ratio = QLineF(center, initialTransform.inverted().map(mousePos)).length() /QLineF(center, initial).length(); //line from center to handle 1 * difference. //set handle1 translated to // valid ellipse gc.setTransform(initialTransform); gc.setTransform(m_ellipse.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), m_ellipse.semiMajor()*Ratio, m_ellipse.semiMinor()*Ratio); drawPreview(gc, path); } } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void ConcentricEllipseAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || handles().size() < 2) { // 2 points means a line, so we can continue after 1 point return; } QTransform initialTransform = converter->documentToWidgetTransform(); if (handles().size() == 2) { // just draw the axis gc.setTransform(initialTransform); QPainterPath path; path.moveTo(*handles()[0]); path.lineTo(*handles()[1]); drawPath(gc, path, isSnappingActive()); return; } if (m_ellipse.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(m_ellipse.getInverse(), true); QPainterPath path; path.moveTo(QPointF(-m_ellipse.semiMajor(), 0)); path.lineTo(QPointF(m_ellipse.semiMajor(), 0)); path.moveTo(QPointF(0, -m_ellipse.semiMinor())); path.lineTo(QPointF(0, m_ellipse.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), m_ellipse.semiMajor(), m_ellipse.semiMinor()); drawPath(gc, path, isSnappingActive()); } } QRect ConcentricEllipseAssistant::boundingRect() const { if (!isAssistantComplete()) { return KisPaintingAssistant::boundingRect(); } if (m_ellipse.set(*handles()[0], *handles()[1], *handles()[2])) { return m_ellipse.boundingRect().adjusted(-2, -2, 2, 2).toAlignedRect(); } else { return QRect(); } } QPointF ConcentricEllipseAssistant::buttonPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool ConcentricEllipseAssistant::isAssistantComplete() const { return handles().size() >= 3; } ConcentricEllipseAssistantFactory::ConcentricEllipseAssistantFactory() { } ConcentricEllipseAssistantFactory::~ConcentricEllipseAssistantFactory() { } QString ConcentricEllipseAssistantFactory::id() const { return "concentric ellipse"; } QString ConcentricEllipseAssistantFactory::name() const { return i18n("Concentric Ellipse"); } KisPaintingAssistant* ConcentricEllipseAssistantFactory::createPaintingAssistant() const { return new ConcentricEllipseAssistant; } diff --git a/plugins/assistants/Assistants/ConcentricEllipseAssistant.h b/plugins/assistants/Assistants/ConcentricEllipseAssistant.h index 1a0680eb38..55200afa59 100644 --- a/plugins/assistants/Assistants/ConcentricEllipseAssistant.h +++ b/plugins/assistants/Assistants/ConcentricEllipseAssistant.h @@ -1,59 +1,60 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _CONCENTRIC_ELLIPSE_ASSISTANT_H_ #define _CONCENTRIC_ELLIPSE_ASSISTANT_H_ #include "kis_painting_assistant.h" #include "Ellipse.h" #include #include class ConcentricEllipseAssistant : public KisPaintingAssistant { public: ConcentricEllipseAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; QPointF buttonPosition() const override; int numHandles() const override { return 3; } bool isAssistantComplete() const override; protected: QRect boundingRect() const override; void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin) const; mutable Ellipse m_ellipse; mutable Ellipse m_extraEllipse; explicit ConcentricEllipseAssistant(const ConcentricEllipseAssistant &rhs, QMap &handleMap); }; class ConcentricEllipseAssistantFactory : public KisPaintingAssistantFactory { public: ConcentricEllipseAssistantFactory(); ~ConcentricEllipseAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/Ellipse.cc b/plugins/assistants/Assistants/Ellipse.cc index 1acdd7aedf..5ad1f7d12b 100644 --- a/plugins/assistants/Assistants/Ellipse.cc +++ b/plugins/assistants/Assistants/Ellipse.cc @@ -1,192 +1,193 @@ /* * Copyright (c) 2010 Geoffry Song * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Ellipse.h" #include #include Ellipse::Ellipse() : a(-1), b(-1) { } Ellipse::Ellipse(const QPointF& _p1, const QPointF& _p2, const QPointF& _p3) : p1(_p1), p2(_p2), p3(_p3) { changeMajor(); } Ellipse::~Ellipse() { } bool Ellipse::set(const QPointF& m1, const QPointF& m2, const QPointF& p) { bool changedMajor = m1 != p1 || m2 != p2, changedMinor = !changedMajor && p != p3; p1 = m1; p2 = m2; p3 = p; if (changedMajor) { return changeMajor(); } else if (changedMinor) { return changeMinor(); } else { return a > 0 && b > 0; } } QPointF Ellipse::project(const QPointF& pt) const { if (a <= 0 || b <= 0) return pt; // not a valid ellipse QPointF p = matrix.map(pt); /* * intersect line from (0,0) to p with the ellipse in canonical position * the equation of the line is y = py/px x * the equation of the ellipse is x^2/a^2 + y^2/b^2 = 1 * x=(a*b*px)/sqrt(a^2*py^2+b^2*px^2) * y=(a*b*py)/sqrt(a^2*py^2+b^2*px^2) */ const qreal divisor = sqrt(a * a * p.y() * p.y() + b * b * p.x() * p.x()); if (divisor <= 0) return inverse.map(QPointF(a, 0)); // give up const qreal ab = a * b, factor = 1.0 / divisor; QPointF ep(ab * p.x() * factor, ab * p.y() * factor); return inverse.map(ep); /* return inverse.map(closest(matrix.map(pt)));*/ } inline QPointF rotate90(const QPointF& p) { return QPointF(p.y(), -p.x()); } QRectF Ellipse::boundingRect() const { const QPointF d = rotate90((p2 - p1) * 0.5 * b / a); const QPointF pts[4] = { p1 + d, p1 - d, p2 + d, p2 - d }; QRectF ret; for (int i = 0; i < 4; ++i) { ret = ret.united(QRectF(pts[i], QSizeF(0.0001, 0.0001))); } return ret; } inline qreal sqrlength(const QPointF& vec) { return vec.x() * vec.x() + vec.y() * vec.y(); } inline qreal length(const QPointF& vec) { return sqrt(vec.x() * vec.x() + vec.y() * vec.y()); } bool Ellipse::changeMajor() { a = length(p1 - p2) * 0.5; /* * calculate transformation matrix * x' = m11*x + m21*y + dx * y' = m22*y + m12*x + dy * m11 = m22, m12 = -m21 (rotations and translations only) * x' = m11*x - m12*y + dx * y' = m11*y + m12*x + dy * * then, transforming (x1, y1) to (x1', y1') and (x2, y2) to (x2', y2'): * * m11 = (y2*y2' + y1 * (y1'-y2') - y2*y1' + x2*x'2 - x1*x'2 + (x1-x2)*x1') * ------------------------------------------------------------------ * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2) * m12 = -(x1*(y2'-y1') - x2*y2' + x2*y1' + x2'*y2 - x1'*y2 + (x1'-x2')*y1) * ------------------------------------------------------------------ * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2) * dx = (x1*(-y2*y2' + y2*y1' - x2*x2') + y1*( x2*y2' - x2*y1' - x2'*y2 - x1'*y2) + x2'*y1^2 + x1^2*x2' + x1'*(y2^2 + x2^2 - x1*x2)) * ---------------------------------------------------------------------------------------------------------------------------- * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2) * dy = (x1*(-x2*y2' - x2*y1' + x2'*y2) + y1*(-y2*y2' - y2*y1' - x2*x2' + x2*x1') + y2'*y1^2 + x1^2*y2' + y1'(y2^2 + x2^2) - x1*x1'*y2) * ------------------------------------------------------------------------------------------------------------------------------- * (y2^2 - 2*y1*y2 + y1^2 + x2^2 - 2*x1*x2 + x1^2) * * in our usage, to move the ellipse into canonical position: * * (x1, y1) = p1 * (x2, y2) = p2 * (x1', y1') = (-a, 0) * (x2', y2') = (a, 0) */ const qreal x1 = p1.x(), x2 = p2.x(), y1 = p1.y(), y2 = p2.y(), x1p = -a, x2p = a, x1sqr = x1 * x1, x2sqr = x2 * x2, y1sqr = y1 * y1, y2sqr = y2 * y2, factor = 1.0 / (x1sqr + y1sqr + x2sqr + y2sqr - 2.0 * y1 * y2 - 2.0 * x1 * x2), m11 = (x2*x2p - x1*x2p + (x1-x2)*x1p) * factor, m12 = -(x2p*y2 - x1p*y2 + (x1p-x2p)*y1) * factor, dx = (x1*(-x2*x2p) + y1*(-x2p*y2 - x1p*y2) + x2p*y1sqr + x1sqr*x2p + x1p*(y2sqr + x2sqr - x1*x2)) * factor, dy = (x1*(x2p*y2) + y1*(-x2*x2p + x2*x1p) - x1*x1p*y2) * factor; matrix = QTransform(m11, m12, -m12, m11, dx, dy); inverse = matrix.inverted(); return changeMinor(); } bool Ellipse::changeMinor() { QPointF p = matrix.map(p3); /* * ellipse formula: * x^2/a^2 + y^2/b^2 = 1 * b = sqrt(y^2 / (1 - x^2/a^2)) */ const qreal asqr = a * a, xsqr = p.x() * p.x(), ysqr = p.y() * p.y(), divisor = (1.0 - xsqr / asqr); if (divisor <= 0) { // division by zero! b = -1; return false; } b = sqrt(ysqr / divisor); return true; } bool Ellipse::setMajor1(const QPointF& p) { p1 = p; return changeMajor(); } bool Ellipse::setMajor2(const QPointF& p) { p2 = p; return changeMajor(); } bool Ellipse::setPoint(const QPointF& p) { p3 = p; return changeMinor(); } diff --git a/plugins/assistants/Assistants/Ellipse.h b/plugins/assistants/Assistants/Ellipse.h index 24dcecdeb2..843616e2e7 100644 --- a/plugins/assistants/Assistants/Ellipse.h +++ b/plugins/assistants/Assistants/Ellipse.h @@ -1,62 +1,63 @@ /* * Copyright (c) 2010 Geoffry Song * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ELLIPSE_H_ #define _ELLIPSE_H_ #include #include class Ellipse { public: Ellipse(); Ellipse(const QPointF& p1, const QPointF& p2, const QPointF& p3); ~Ellipse(); QPointF project(const QPointF&) const; // find a close point on the ellipse QRectF boundingRect() const; // find an axis-aligned box bounding this ellipse (inexact) bool set(const QPointF& m1, const QPointF& m2, const QPointF& p); // set all points const QPointF& major1() const { return p1; } bool setMajor1(const QPointF& p); const QPointF& major2() const { return p2; } bool setMajor2(const QPointF& p); const QPointF& point() const { return p3; } bool setPoint(const QPointF& p); const QTransform& getTransform() const { return matrix; } const QTransform& getInverse() const { return inverse; } qreal semiMajor() const { return a; } qreal semiMinor() const { return b; } private: bool changeMajor(); // determine 'a', 'b', 'matrix' and 'inverse' bool changeMinor(); // determine 'b' QTransform matrix; // transformation turning p1, p2 and p3 into their corresponding points on the ellipse in canonical position QTransform inverse; // inverse transformation qreal a; // semi-major axis: half the distance between p1 and p2 (horizontal axis) qreal b; // semi-minor axis (vertical axis) // a may not actually be larger than b, but we don't care that much QPointF p1; QPointF p2; QPointF p3; }; #endif diff --git a/plugins/assistants/Assistants/EllipseAssistant.cc b/plugins/assistants/Assistants/EllipseAssistant.cc index afee5c713b..4431886c15 100644 --- a/plugins/assistants/Assistants/EllipseAssistant.cc +++ b/plugins/assistants/Assistants/EllipseAssistant.cc @@ -1,180 +1,181 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "EllipseAssistant.h" #include #include "kis_debug.h" #include #include #include #include #include #include EllipseAssistant::EllipseAssistant() : KisPaintingAssistant("ellipse", i18n("Ellipse assistant")) { } EllipseAssistant::EllipseAssistant(const EllipseAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , e(rhs.e) { } KisPaintingAssistantSP EllipseAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new EllipseAssistant(*this, handleMap)); } QPointF EllipseAssistant::project(const QPointF& pt) const { Q_ASSERT(isAssistantComplete()); e.set(*handles()[0], *handles()[1], *handles()[2]); return e.project(pt); } QPointF EllipseAssistant::adjustPosition(const QPointF& pt, const QPointF& /*strokeBegin*/) { return project(pt); } void EllipseAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPoint mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in the ellipse assistant, you may have passed arguments incorrectly:"<documentToWidgetTransform(); if (isSnappingActive() && boundingRect().contains(initialTransform.inverted().map(mousePos), false) && previewVisible==true){ if (isAssistantComplete()){ if (e.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; //path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0)); //path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor()); drawPreview(gc, path); } } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void EllipseAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || handles().size() < 2){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); if (handles().size() == 2) { // just draw the axis gc.setTransform(initialTransform); QPainterPath path; path.moveTo(*handles()[0]); path.lineTo(*handles()[1]); drawPath(gc, path, isSnappingActive()); return; } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0)); path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor()); drawPath(gc, path, isSnappingActive()); } } QRect EllipseAssistant::boundingRect() const { if (!isAssistantComplete()) { return KisPaintingAssistant::boundingRect(); } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { return e.boundingRect().adjusted(-2, -2, 2, 2).toAlignedRect(); } else { return QRect(); } } QPointF EllipseAssistant::buttonPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool EllipseAssistant::isAssistantComplete() const { return handles().size() >= 3; } EllipseAssistantFactory::EllipseAssistantFactory() { } EllipseAssistantFactory::~EllipseAssistantFactory() { } QString EllipseAssistantFactory::id() const { return "ellipse"; } QString EllipseAssistantFactory::name() const { return i18n("Ellipse"); } KisPaintingAssistant* EllipseAssistantFactory::createPaintingAssistant() const { return new EllipseAssistant; } diff --git a/plugins/assistants/Assistants/EllipseAssistant.h b/plugins/assistants/Assistants/EllipseAssistant.h index 71283ef046..6e3b6b02aa 100644 --- a/plugins/assistants/Assistants/EllipseAssistant.h +++ b/plugins/assistants/Assistants/EllipseAssistant.h @@ -1,57 +1,58 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ELLIPSE_ASSISTANT_H_ #define _ELLIPSE_ASSISTANT_H_ #include "kis_painting_assistant.h" #include "Ellipse.h" #include class EllipseAssistant : public KisPaintingAssistant { public: EllipseAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; QPointF buttonPosition() const override; int numHandles() const override { return 3; } bool isAssistantComplete() const override; protected: QRect boundingRect() const override; void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt) const; explicit EllipseAssistant(const EllipseAssistant &rhs, QMap &handleMap); mutable Ellipse e; }; class EllipseAssistantFactory : public KisPaintingAssistantFactory { public: EllipseAssistantFactory(); ~EllipseAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/FisheyePointAssistant.cc b/plugins/assistants/Assistants/FisheyePointAssistant.cc index f97c8a59d9..610f2664fa 100644 --- a/plugins/assistants/Assistants/FisheyePointAssistant.cc +++ b/plugins/assistants/Assistants/FisheyePointAssistant.cc @@ -1,237 +1,238 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "FisheyePointAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include FisheyePointAssistant::FisheyePointAssistant() : KisPaintingAssistant("fisheye-point", i18n("Fish Eye Point assistant")) { } FisheyePointAssistant::FisheyePointAssistant(const FisheyePointAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , e(rhs.e) , extraE(rhs.extraE) { } KisPaintingAssistantSP FisheyePointAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new FisheyePointAssistant(*this, handleMap)); } QPointF FisheyePointAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { const static QPointF nullPoint(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); Q_ASSERT(isAssistantComplete()); e.set(*handles()[0], *handles()[1], *handles()[2]); qreal dx = pt.x() - strokeBegin.x(), dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //set the extrapolation ellipse. if (e.set(*handles()[0], *handles()[1], *handles()[2])){ QLineF radius(*handles()[1], *handles()[0]); radius.setAngle(fmod(radius.angle()+180.0,360.0)); QLineF radius2(*handles()[0], *handles()[1]); radius2.setAngle(fmod(radius2.angle()+180.0,360.0)); if ( extraE.set(*handles()[0], *handles()[1],strokeBegin ) ) { return extraE.project(pt); } else if (extraE.set(radius.p1(), radius.p2(),strokeBegin)) { return extraE.project(pt); } else if (extraE.set(radius2.p1(), radius2.p2(),strokeBegin)){ return extraE.project(pt); } } return nullPoint; } QPointF FisheyePointAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin) { return project(pt, strokeBegin); } void FisheyePointAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPointF mousePos(0,0); if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); if (isSnappingActive() && previewVisible == true ) { if (isAssistantComplete()){ if (e.set(*handles()[0], *handles()[1], *handles()[2])) { if (extraE.set(*handles()[0], *handles()[1], initialTransform.inverted().map(mousePos))){ gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor()); drawPreview(gc, path); } QLineF radius(*handles()[1], *handles()[0]); radius.setAngle(fmod(radius.angle()+180.0,360.0)); if (extraE.set(radius.p1(), radius.p2(), initialTransform.inverted().map(mousePos))){ gc.setTransform(initialTransform); gc.setTransform(extraE.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor()); drawPreview(gc, path); } QLineF radius2(*handles()[0], *handles()[1]); radius2.setAngle(fmod(radius2.angle()+180.0,360.0)); if (extraE.set(radius2.p1(), radius2.p2(), initialTransform.inverted().map(mousePos))){ gc.setTransform(initialTransform); gc.setTransform(extraE.getInverse(), true); QPainterPath path; // Draw the ellipse path.addEllipse(QPointF(0, 0), extraE.semiMajor(), extraE.semiMinor()); drawPreview(gc, path); } } } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void FisheyePointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); if (handles().size() == 2) { // just draw the axis gc.setTransform(initialTransform); QPainterPath path; path.moveTo(*handles()[0]); path.lineTo(*handles()[1]); drawPath(gc, path, isSnappingActive()); return; } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { // valid ellipse gc.setTransform(initialTransform); gc.setTransform(e.getInverse(), true); QPainterPath path; //path.moveTo(QPointF(-e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), -e.semiMinor())); path.moveTo(QPointF(-e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(-e.semiMajor(), e.semiMinor())); //path.moveTo(QPointF(-e.semiMajor(), e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), e.semiMinor())); path.moveTo(QPointF(e.semiMajor(), -e.semiMinor())); path.lineTo(QPointF(e.semiMajor(), e.semiMinor())); path.moveTo(QPointF(-(e.semiMajor()*3), -e.semiMinor())); path.lineTo(QPointF(-(e.semiMajor()*3), e.semiMinor())); path.moveTo(QPointF((e.semiMajor()*3), -e.semiMinor())); path.lineTo(QPointF((e.semiMajor()*3), e.semiMinor())); path.moveTo(QPointF(-e.semiMajor(), 0)); path.lineTo(QPointF(e.semiMajor(), 0)); //path.moveTo(QPointF(0, -e.semiMinor())); path.lineTo(QPointF(0, e.semiMinor())); // Draw the ellipse path.addEllipse(QPointF(0, 0), e.semiMajor(), e.semiMinor()); drawPath(gc, path, isSnappingActive()); } } QRect FisheyePointAssistant::boundingRect() const { if (!isAssistantComplete()) { return KisPaintingAssistant::boundingRect(); } if (e.set(*handles()[0], *handles()[1], *handles()[2])) { return e.boundingRect().adjusted(-(e.semiMajor()*2), -2, (e.semiMajor()*2), 2).toAlignedRect(); } else { return QRect(); } } QPointF FisheyePointAssistant::buttonPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool FisheyePointAssistant::isAssistantComplete() const { return handles().size() >= 3; } FisheyePointAssistantFactory::FisheyePointAssistantFactory() { } FisheyePointAssistantFactory::~FisheyePointAssistantFactory() { } QString FisheyePointAssistantFactory::id() const { return "fisheye-point"; } QString FisheyePointAssistantFactory::name() const { return i18n("Fish Eye Point"); } KisPaintingAssistant* FisheyePointAssistantFactory::createPaintingAssistant() const { return new FisheyePointAssistant; } diff --git a/plugins/assistants/Assistants/FisheyePointAssistant.h b/plugins/assistants/Assistants/FisheyePointAssistant.h index 606d5b107d..f2a27d5a48 100644 --- a/plugins/assistants/Assistants/FisheyePointAssistant.h +++ b/plugins/assistants/Assistants/FisheyePointAssistant.h @@ -1,65 +1,66 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _FISHEYEPOINT_ASSISTANT_H_ #define _FISHEYEPOINT_ASSISTANT_H_ #include "kis_painting_assistant.h" #include "Ellipse.h" #include #include #include #include //class FisheyePoint; class FisheyePointAssistant : public KisPaintingAssistant { public: FisheyePointAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); QPointF buttonPosition() const override; int numHandles() const override { return 3; } bool isAssistantComplete() const override; protected: QRect boundingRect() const override; void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit FisheyePointAssistant(const FisheyePointAssistant &rhs, QMap &handleMap); mutable Ellipse e; mutable Ellipse extraE; }; class FisheyePointAssistantFactory : public KisPaintingAssistantFactory { public: FisheyePointAssistantFactory(); ~FisheyePointAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/InfiniteRulerAssistant.cc b/plugins/assistants/Assistants/InfiniteRulerAssistant.cc index 4ce58eedff..f85a188367 100644 --- a/plugins/assistants/Assistants/InfiniteRulerAssistant.cc +++ b/plugins/assistants/Assistants/InfiniteRulerAssistant.cc @@ -1,170 +1,171 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "InfiniteRulerAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include InfiniteRulerAssistant::InfiniteRulerAssistant() : KisPaintingAssistant("infinite ruler", i18n("Infinite Ruler assistant")) { } InfiniteRulerAssistant::InfiniteRulerAssistant(const InfiniteRulerAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) { } KisPaintingAssistantSP InfiniteRulerAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new InfiniteRulerAssistant(*this, handleMap)); } QPointF InfiniteRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { Q_ASSERT(isAssistantComplete()); //code nicked from the perspective ruler. qreal dx = pt.x() - strokeBegin.x(), dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //dbgKrita<canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1])); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); QPainterPath path; path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); drawPreview(gc, path);//and we draw the preview. } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void InfiniteRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || !isAssistantComplete()){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); // Draw the line QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); drawPath(gc, path, isSnappingActive()); } QPointF InfiniteRulerAssistant::buttonPosition() const { return (*handles()[0]); } bool InfiniteRulerAssistant::isAssistantComplete() const { return handles().size() >= 2; } InfiniteRulerAssistantFactory::InfiniteRulerAssistantFactory() { } InfiniteRulerAssistantFactory::~InfiniteRulerAssistantFactory() { } QString InfiniteRulerAssistantFactory::id() const { return "infinite ruler"; } QString InfiniteRulerAssistantFactory::name() const { return i18n("Infinite Ruler"); } KisPaintingAssistant* InfiniteRulerAssistantFactory::createPaintingAssistant() const { return new InfiniteRulerAssistant; } diff --git a/plugins/assistants/Assistants/InfiniteRulerAssistant.h b/plugins/assistants/Assistants/InfiniteRulerAssistant.h index 3e069635d1..db639da29c 100644 --- a/plugins/assistants/Assistants/InfiniteRulerAssistant.h +++ b/plugins/assistants/Assistants/InfiniteRulerAssistant.h @@ -1,62 +1,63 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _INFINITERULER_ASSISTANT_H_ #define _INFINITERULER_ASSISTANT_H_ #include "kis_painting_assistant.h" #include #include #include #include /* Design: */ class InfiniteRuler; class InfiniteRulerAssistant : public KisPaintingAssistant { public: InfiniteRulerAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); QPointF buttonPosition() const override; int numHandles() const override { return 2; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit InfiniteRulerAssistant(const InfiniteRulerAssistant &rhs, QMap &handleMap); }; class InfiniteRulerAssistantFactory : public KisPaintingAssistantFactory { public: InfiniteRulerAssistantFactory(); ~InfiniteRulerAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/ParallelRulerAssistant.cc b/plugins/assistants/Assistants/ParallelRulerAssistant.cc index 766da9b21e..2d1fa2a943 100644 --- a/plugins/assistants/Assistants/ParallelRulerAssistant.cc +++ b/plugins/assistants/Assistants/ParallelRulerAssistant.cc @@ -1,177 +1,178 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ParallelRulerAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include ParallelRulerAssistant::ParallelRulerAssistant() : KisPaintingAssistant("parallel ruler", i18n("Parallel Ruler assistant")) { } KisPaintingAssistantSP ParallelRulerAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new ParallelRulerAssistant(*this, handleMap)); } ParallelRulerAssistant::ParallelRulerAssistant(const ParallelRulerAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) { } QPointF ParallelRulerAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { Q_ASSERT(isAssistantComplete()); //code nicked from the perspective ruler. qreal dx = pt.x() - strokeBegin.x(); qreal dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { return strokeBegin; // allow some movement before snapping } //dbgKrita<canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); QLineF snapLine= QLineF(initialTransform.map(*handles()[0]), initialTransform.map(*handles()[1])); QPointF translation = (initialTransform.map(*handles()[0])-mousePos)*-1.0; snapLine= snapLine.translated(translation); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); QPainterPath path; path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); drawPreview(gc, path);//and we draw the preview. } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void ParallelRulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || !isAssistantComplete()){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); // Draw the line QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); drawPath(gc, path, isSnappingActive()); } QPointF ParallelRulerAssistant::buttonPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool ParallelRulerAssistant::isAssistantComplete() const { return handles().size() >= 2; } ParallelRulerAssistantFactory::ParallelRulerAssistantFactory() { } ParallelRulerAssistantFactory::~ParallelRulerAssistantFactory() { } QString ParallelRulerAssistantFactory::id() const { return "parallel ruler"; } QString ParallelRulerAssistantFactory::name() const { return i18n("Parallel Ruler"); } KisPaintingAssistant* ParallelRulerAssistantFactory::createPaintingAssistant() const { return new ParallelRulerAssistant; } diff --git a/plugins/assistants/Assistants/ParallelRulerAssistant.h b/plugins/assistants/Assistants/ParallelRulerAssistant.h index ece6cf38a7..5c82ad7246 100644 --- a/plugins/assistants/Assistants/ParallelRulerAssistant.h +++ b/plugins/assistants/Assistants/ParallelRulerAssistant.h @@ -1,62 +1,63 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PARALLELRULER_ASSISTANT_H_ #define _PARALLELRULER_ASSISTANT_H_ #include "kis_painting_assistant.h" #include #include #include #include /* Design: */ class ParallelRuler; class ParallelRulerAssistant : public KisPaintingAssistant { public: ParallelRulerAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); QPointF buttonPosition() const override; int numHandles() const override { return 2; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit ParallelRulerAssistant(const ParallelRulerAssistant &rhs, QMap &handleMap); }; class ParallelRulerAssistantFactory : public KisPaintingAssistantFactory { public: ParallelRulerAssistantFactory(); ~ParallelRulerAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/PerspectiveAssistant.cc b/plugins/assistants/Assistants/PerspectiveAssistant.cc index 4d920ecf7f..8e0c327e16 100644 --- a/plugins/assistants/Assistants/PerspectiveAssistant.cc +++ b/plugins/assistants/Assistants/PerspectiveAssistant.cc @@ -1,480 +1,481 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PerspectiveAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include PerspectiveAssistant::PerspectiveAssistant(QObject *parent) : KisAbstractPerspectiveGrid(parent) , KisPaintingAssistant("perspective", i18n("Perspective assistant")) { } PerspectiveAssistant::PerspectiveAssistant(const PerspectiveAssistant &rhs, QMap &handleMap) : KisAbstractPerspectiveGrid(rhs.parent()) , KisPaintingAssistant(rhs, handleMap) , m_snapLine(rhs.m_snapLine) , m_cachedTransform(rhs.m_cachedTransform) , m_cachedPolygon(rhs.m_cachedPolygon) , m_cacheValid(rhs.m_cacheValid) { for (int i = 0; i < 4; ++i) { m_cachedPoints[i] = rhs.m_cachedPoints[i]; } } KisPaintingAssistantSP PerspectiveAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new PerspectiveAssistant(*this, handleMap)); } // squared distance from a point to a line inline qreal distsqr(const QPointF& pt, const QLineF& line) { // distance = |(p2 - p1) x (p1 - pt)| / |p2 - p1| // magnitude of (p2 - p1) x (p1 - pt) const qreal cross = (line.dx() * (line.y1() - pt.y()) - line.dy() * (line.x1() - pt.x())); return cross * cross / (line.dx() * line.dx() + line.dy() * line.dy()); } QPointF PerspectiveAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { const static QPointF nullPoint(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); Q_ASSERT(isAssistantComplete()); if (m_snapLine.isNull()) { QPolygonF poly; QTransform transform; if (!getTransform(poly, transform)) { return nullPoint; } if (!poly.containsPoint(strokeBegin, Qt::OddEvenFill)) { return nullPoint; // avoid problems with multiple assistants: only snap if starting in the grid } const qreal dx = pt.x() - strokeBegin.x(); const qreal dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { return strokeBegin; // allow some movement before snapping } // construct transformation bool invertible; const QTransform inverse = transform.inverted(&invertible); if (!invertible) { return nullPoint; // shouldn't happen } // figure out which direction to go const QPointF start = inverse.map(strokeBegin); const QLineF verticalLine = QLineF(strokeBegin, transform.map(start + QPointF(0, 1))); const QLineF horizontalLine = QLineF(strokeBegin, transform.map(start + QPointF(1, 0))); // determine whether the horizontal or vertical line is closer to the point m_snapLine = distsqr(pt, verticalLine) < distsqr(pt, horizontalLine) ? verticalLine : horizontalLine; } // snap to line const qreal dx = m_snapLine.dx(), dy = m_snapLine.dy(), dx2 = dx * dx, dy2 = dy * dy, invsqrlen = 1.0 / (dx2 + dy2); QPointF r(dx2 * pt.x() + dy2 * m_snapLine.x1() + dx * dy * (pt.y() - m_snapLine.y1()), dx2 * m_snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - m_snapLine.x1())); r *= invsqrlen; return r; } QPointF PerspectiveAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin) { return project(pt, strokeBegin); } void PerspectiveAssistant::endStroke() { m_snapLine = QLineF(); } bool PerspectiveAssistant::contains(const QPointF& pt) const { QPolygonF poly; if (!quad(poly)) return false; return poly.containsPoint(pt, Qt::OddEvenFill); } inline qreal lengthSquared(const QPointF& vector) { return vector.x() * vector.x() + vector.y() * vector.y(); } inline qreal localScale(const QTransform& transform, QPointF pt) { // const qreal epsilon = 1e-5, epsilonSquared = epsilon * epsilon; // qreal xSizeSquared = lengthSquared(transform.map(pt + QPointF(epsilon, 0.0)) - orig) / epsilonSquared; // qreal ySizeSquared = lengthSquared(transform.map(pt + QPointF(0.0, epsilon)) - orig) / epsilonSquared; // xSizeSquared /= lengthSquared(transform.map(QPointF(0.0, pt.y())) - transform.map(QPointF(1.0, pt.y()))); // ySizeSquared /= lengthSquared(transform.map(QPointF(pt.x(), 0.0)) - transform.map(QPointF(pt.x(), 1.0))); // when taking the limit epsilon->0: // xSizeSquared=((m23*y+m33)^2*(m23*y+m33+m13)^2)/(m23*y+m13*x+m33)^4 // ySizeSquared=((m23*y+m33)^2*(m23*y+m33+m13)^2)/(m23*y+m13*x+m33)^4 // xSize*ySize=(abs(m13*x+m33)*abs(m13*x+m33+m23)*abs(m23*y+m33)*abs(m23*y+m33+m13))/(m23*y+m13*x+m33)^4 const qreal x = transform.m13() * pt.x(), y = transform.m23() * pt.y(), a = x + transform.m33(), b = y + transform.m33(), c = x + y + transform.m33(), d = c * c; return fabs(a*(a + transform.m23())*b*(b + transform.m13()))/(d * d); } // returns the reciprocal of the maximum local scale at the points (0,0),(0,1),(1,0),(1,1) inline qreal inverseMaxLocalScale(const QTransform& transform) { const qreal a = fabs((transform.m33() + transform.m13()) * (transform.m33() + transform.m23())), b = fabs((transform.m33()) * (transform.m13() + transform.m33() + transform.m23())), d00 = transform.m33() * transform.m33(), d11 = (transform.m33() + transform.m23() + transform.m13())*(transform.m33() + transform.m23() + transform.m13()), s0011 = qMin(d00, d11) / a, d10 = (transform.m33() + transform.m13()) * (transform.m33() + transform.m13()), d01 = (transform.m33() + transform.m23()) * (transform.m33() + transform.m23()), s1001 = qMin(d10, d01) / b; return qMin(s0011, s1001); } qreal PerspectiveAssistant::distance(const QPointF& pt) const { QPolygonF poly; QTransform transform; if (!getTransform(poly, transform)) { return 1.0; } bool invertible; QTransform inverse = transform.inverted(&invertible); if (!invertible) { return 1.0; } if (inverse.m13() * pt.x() + inverse.m23() * pt.y() + inverse.m33() == 0.0) { return 0.0; // point at infinity } return localScale(transform, inverse.map(pt)) * inverseMaxLocalScale(transform); } // draw a vanishing point marker inline QPainterPath drawX(const QPointF& pt) { QPainterPath path; path.moveTo(QPointF(pt.x() - 5.0, pt.y() - 5.0)); path.lineTo(QPointF(pt.x() + 5.0, pt.y() + 5.0)); path.moveTo(QPointF(pt.x() - 5.0, pt.y() + 5.0)); path.lineTo(QPointF(pt.x() + 5.0, pt.y() - 5.0)); return path; } void PerspectiveAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QTransform initialTransform = converter->documentToWidgetTransform(); //QTransform reverseTransform = converter->widgetToDocument(); QPolygonF poly; QTransform transform; // unused, but computed for caching purposes if (getTransform(poly, transform) && assistantVisible==true) { // draw vanishing points QPointF intersection(0, 0); if (fmod(QLineF(poly[0], poly[1]).angle(), 180.0)>=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)+2.0 || fmod(QLineF(poly[0], poly[1]).angle(), 180.0)<=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)-2.0) { if (QLineF(poly[0], poly[1]).intersect(QLineF(poly[2], poly[3]), &intersection) != QLineF::NoIntersection) { drawPath(gc, drawX(initialTransform.map(intersection))); } } if (fmod(QLineF(poly[1], poly[2]).angle(), 180.0)>=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)+2.0 || fmod(QLineF(poly[1], poly[2]).angle(), 180.0)<=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)-2.0){ if (QLineF(poly[1], poly[2]).intersect(QLineF(poly[3], poly[0]), &intersection) != QLineF::NoIntersection) { drawPath(gc, drawX(initialTransform.map(intersection))); } } } if (isSnappingActive() && getTransform(poly, transform) && previewVisible==true){ //find vanishing point, find mouse, draw line between both. QPainterPath path2; QPointF intersection(0, 0);//this is the position of the vanishing point. QPointF mousePos(0,0); QLineF snapLine; QRect viewport= gc.viewport(); QRect bounds; if (canvas){ //simplest, cheapest way to get the mouse-position mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that. mousePos = QCursor::pos(); // this'll give an offset dbgFile<<"canvas does not exist, you may have passed arguments incorrectly:"<=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)+2.0 || fmod(QLineF(poly[0], poly[1]).angle(), 180.0)<=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)-2.0) { if (QLineF(poly[0], poly[1]).intersect(QLineF(poly[2], poly[3]), &intersection) != QLineF::NoIntersection) { intersectTransformed = initialTransform.map(intersection); snapLine = QLineF(intersectTransformed, mousePos); KisAlgebra2D::intersectLineRect(snapLine, viewport); bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint()); QPainterPath path; if (bounds.contains(intersectTransformed.toPoint())){ path2.moveTo(intersectTransformed); path2.lineTo(snapLine.p1()); } else { path2.moveTo(snapLine.p1()); path2.lineTo(snapLine.p2()); } } } if (fmod(QLineF(poly[1], poly[2]).angle(), 180.0)>=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)+2.0 || fmod(QLineF(poly[1], poly[2]).angle(), 180.0)<=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)-2.0){ if (QLineF(poly[1], poly[2]).intersect(QLineF(poly[3], poly[0]), &intersection) != QLineF::NoIntersection) { intersectTransformed = initialTransform.map(intersection); snapLine = QLineF(intersectTransformed, mousePos); KisAlgebra2D::intersectLineRect(snapLine, viewport); bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint()); QPainterPath path; if (bounds.contains(intersectTransformed.toPoint())){ path2.moveTo(intersectTransformed); path2.lineTo(snapLine.p1()); } else { path2.moveTo(snapLine.p1()); path2.lineTo(snapLine.p2()); } } } drawPreview(gc, path2); } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached,canvas, assistantVisible, previewVisible); } void PerspectiveAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false) { return; } gc.setTransform(converter->documentToWidgetTransform()); QPolygonF poly; QTransform transform; if (!getTransform(poly, transform)) { // color red for an invalid transform, but not for an incomplete one if(isAssistantComplete()) { gc.setPen(QColor(255, 0, 0, 125)); gc.drawPolygon(poly); } else { QPainterPath path; path.addPolygon(poly); drawPath(gc, path, isSnappingActive()); } } else { gc.setPen(QColor(0, 0, 0, 125)); gc.setTransform(transform, true); QPainterPath path; for (int y = 0; y <= 8; ++y) { path.moveTo(QPointF(0.0, y * 0.125)); path.lineTo(QPointF(1.0, y * 0.125)); } for (int x = 0; x <= 8; ++x) { path.moveTo(QPointF(x * 0.125, 0.0)); path.lineTo(QPointF(x * 0.125, 1.0)); } drawPath(gc, path, isSnappingActive()); } } QPointF PerspectiveAssistant::buttonPosition() const { QPointF centroid(0, 0); for (int i = 0; i < 4; ++i) { centroid += *handles()[i]; } return centroid * 0.25; } template int sign(T a) { return (a > 0) - (a < 0); } // perpendicular dot product inline qreal pdot(const QPointF& a, const QPointF& b) { return a.x() * b.y() - a.y() * b.x(); } bool PerspectiveAssistant::quad(QPolygonF& poly) const { for (int i = 0; i < handles().size(); ++i) { poly.push_back(*handles()[i]); } if (!isAssistantComplete()) { return false; } int sum = 0; int signs[4]; for (int i = 0; i < 4; ++i) { int j = (i == 3) ? 0 : (i + 1); int k = (j == 3) ? 0 : (j + 1); signs[i] = sign(pdot(poly[j] - poly[i], poly[k] - poly[j])); sum += signs[i]; } if (sum == 0) { // complex (crossed) for (int i = 0; i < 4; ++i) { int j = (i == 3) ? 0 : (i + 1); if (signs[i] * signs[j] == -1) { // opposite signs: uncross std::swap(poly[i], poly[j]); return true; } } // okay, maybe it's just a line return false; } else if (sum != 4 && sum != -4) { // concave, or a triangle if (sum == 2 || sum == -2) { // concave, let's return a triangle instead for (int i = 0; i < 4; ++i) { int j = (i == 3) ? 0 : (i + 1); if (signs[i] != sign(sum)) { // wrong sign: drop the inside node poly.remove(j); return false; } } } return false; } // convex return true; } bool PerspectiveAssistant::getTransform(QPolygonF& poly, QTransform& transform) const { if (m_cachedPolygon.size() != 0 && isAssistantComplete()) { for (int i = 0; i <= 4; ++i) { if (i == 4) { poly = m_cachedPolygon; transform = m_cachedTransform; return m_cacheValid; } if (m_cachedPoints[i] != *handles()[i]) break; } } m_cachedPolygon.clear(); m_cacheValid = false; if (!quad(poly)) { m_cachedPolygon = poly; return false; } if (!QTransform::squareToQuad(poly, transform)) { qWarning("Failed to create perspective mapping"); return false; } for (int i = 0; i < 4; ++i) { m_cachedPoints[i] = *handles()[i]; } m_cachedPolygon = poly; m_cachedTransform = transform; m_cacheValid = true; return true; } bool PerspectiveAssistant::isAssistantComplete() const { return handles().size() >= 4; // specify 4 corners to make assistant complete } PerspectiveAssistantFactory::PerspectiveAssistantFactory() { } PerspectiveAssistantFactory::~PerspectiveAssistantFactory() { } QString PerspectiveAssistantFactory::id() const { return "perspective"; } QString PerspectiveAssistantFactory::name() const { return i18n("Perspective"); } KisPaintingAssistant* PerspectiveAssistantFactory::createPaintingAssistant() const { return new PerspectiveAssistant; } diff --git a/plugins/assistants/Assistants/PerspectiveAssistant.h b/plugins/assistants/Assistants/PerspectiveAssistant.h index e53ce1b411..7d4ccac50f 100644 --- a/plugins/assistants/Assistants/PerspectiveAssistant.h +++ b/plugins/assistants/Assistants/PerspectiveAssistant.h @@ -1,77 +1,78 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PERSPECTIVE_ASSISTANT_H_ #define _PERSPECTIVE_ASSISTANT_H_ #include "kis_abstract_perspective_grid.h" #include "kis_painting_assistant.h" #include #include #include #include class PerspectiveAssistant : public KisAbstractPerspectiveGrid, public KisPaintingAssistant { Q_OBJECT public: PerspectiveAssistant(QObject * parent = 0); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; void endStroke() override; QPointF buttonPosition() const override; int numHandles() const override { return 4; } void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; bool contains(const QPointF& point) const override; qreal distance(const QPointF& point) const override; bool isAssistantComplete() const override; protected: void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); // creates the convex hull, returns false if it's not a quadrilateral bool quad(QPolygonF& out) const; // finds the transform from perspective coordinates (a unit square) to the document bool getTransform(QPolygonF& polyOut, QTransform& transformOut) const; explicit PerspectiveAssistant(const PerspectiveAssistant &rhs, QMap &handleMap); // which direction to snap to (in transformed coordinates) QLineF m_snapLine; // cached information mutable QTransform m_cachedTransform; mutable QPolygonF m_cachedPolygon; mutable QPointF m_cachedPoints[4]; mutable bool m_cacheValid; }; class PerspectiveAssistantFactory : public KisPaintingAssistantFactory { public: PerspectiveAssistantFactory(); ~PerspectiveAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/Ruler.cc b/plugins/assistants/Assistants/Ruler.cc index 696b6f3d45..d8513a0304 100644 --- a/plugins/assistants/Assistants/Ruler.cc +++ b/plugins/assistants/Assistants/Ruler.cc @@ -1,50 +1,51 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Ruler.h" Ruler::Ruler() : p1(QPointF(10, 10)), p2(QPointF(100, 190)) { } Ruler::~Ruler() { } QPointF Ruler::project(const QPointF& pt) { double x1 = p1.x(); double y1 = p1.y(); double x2 = p2.x(); double y2 = p2.y(); double a1 = (y2 - y1) / (x2 - x1); double b1 = y1 - x1 * a1; double a2 = (x2 - x1) / (y1 - y2); double b2 = pt.y() - a2 * pt.x(); double xm = (b2 - b1) / (a1 - a2); return QPointF(xm, xm * a1 + b1); } const QPointF& Ruler::point1() const { return p1; } const QPointF& Ruler::point2() const { return p2; } diff --git a/plugins/assistants/Assistants/Ruler.h b/plugins/assistants/Assistants/Ruler.h index c629a6a0af..8fc032e6bb 100644 --- a/plugins/assistants/Assistants/Ruler.h +++ b/plugins/assistants/Assistants/Ruler.h @@ -1,42 +1,43 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _RULER_H_ #define _RULER_H_ #include class Ruler { public: Ruler(); ~Ruler(); QPointF project(const QPointF&); const QPointF& point1() const; void setPoint1(const QPointF& p) { p1 = p; } const QPointF& point2() const; void setPoint2(const QPointF& p) { p2 = p; } private: QPointF p1; QPointF p2; }; #endif diff --git a/plugins/assistants/Assistants/RulerAssistant.cc b/plugins/assistants/Assistants/RulerAssistant.cc index 13f64eff7b..ab03b906cb 100644 --- a/plugins/assistants/Assistants/RulerAssistant.cc +++ b/plugins/assistants/Assistants/RulerAssistant.cc @@ -1,176 +1,177 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "RulerAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include RulerAssistant::RulerAssistant() : KisPaintingAssistant("ruler", i18n("Ruler assistant")) { } KisPaintingAssistantSP RulerAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new RulerAssistant(*this, handleMap)); } RulerAssistant::RulerAssistant(const RulerAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) { } QPointF RulerAssistant::project(const QPointF& pt) const { Q_ASSERT(isAssistantComplete()); QPointF pt1 = *handles()[0]; QPointF pt2 = *handles()[1]; QPointF a = pt - pt1; QPointF u = pt2 - pt1; qreal u_norm = sqrt(u.x() * u.x() + u.y() * u.y()); if(u_norm == 0) return pt; u /= u_norm; double t = a.x() * u.x() + a.y() * u.y(); if(t < 0.0) return pt1; if(t > u_norm) return pt2; return t * u + pt1; } QPointF RulerAssistant::adjustPosition(const QPointF& pt, const QPointF& /*strokeBegin*/) { return project(pt); } inline double angle(const QPointF& p1, const QPointF& p2) { return atan2(p2.y() - p1.y(), p2.x() - p1.x()); } inline double norm2(const QPointF& p) { return sqrt(p.x() * p.x() + p.y() * p.y()); } void RulerAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPointF mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<documentToWidgetTransform(); // first we find the path that our point create. QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); //then we use this path to check the bounding rectangle// if (isSnappingActive() && path.boundingRect().contains(initialTransform.inverted().map(mousePos)) && previewVisible==true){ drawPreview(gc, path);//and we draw the preview. } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void RulerAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || !isAssistantComplete()){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); // Draw the line QPointF p1 = *handles()[0]; QPointF p2 = *handles()[1]; gc.setTransform(initialTransform); QPainterPath path; path.moveTo(p1); path.lineTo(p2); drawPath(gc, path, isSnappingActive()); } QPointF RulerAssistant::buttonPosition() const { return (*handles()[0] + *handles()[1]) * 0.5; } bool RulerAssistant::isAssistantComplete() const { return handles().size() >= 2; } RulerAssistantFactory::RulerAssistantFactory() { } RulerAssistantFactory::~RulerAssistantFactory() { } QString RulerAssistantFactory::id() const { return "ruler"; } QString RulerAssistantFactory::name() const { return i18n("Ruler"); } KisPaintingAssistant* RulerAssistantFactory::createPaintingAssistant() const { return new RulerAssistant; } diff --git a/plugins/assistants/Assistants/RulerAssistant.h b/plugins/assistants/Assistants/RulerAssistant.h index 36fcdf5eb1..5211c69b90 100644 --- a/plugins/assistants/Assistants/RulerAssistant.h +++ b/plugins/assistants/Assistants/RulerAssistant.h @@ -1,56 +1,57 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _RULER_ASSISTANT_H_ #define _RULER_ASSISTANT_H_ #include #include "kis_painting_assistant.h" class Ruler; class RulerAssistant : public KisPaintingAssistant { public: RulerAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; QPointF buttonPosition() const override; int numHandles() const override { return 2; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt) const; explicit RulerAssistant(const RulerAssistant &rhs, QMap &handleMap); }; class RulerAssistantFactory : public KisPaintingAssistantFactory { public: RulerAssistantFactory(); ~RulerAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/SplineAssistant.cc b/plugins/assistants/Assistants/SplineAssistant.cc index 3ad566d5b3..7755f38b2e 100644 --- a/plugins/assistants/Assistants/SplineAssistant.cc +++ b/plugins/assistants/Assistants/SplineAssistant.cc @@ -1,238 +1,239 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "SplineAssistant.h" #include #include #include #include #include #include #include "kis_debug.h" #include #include #include SplineAssistant::SplineAssistant() : KisPaintingAssistant("spline", i18n("Spline assistant")) { } SplineAssistant::SplineAssistant(const SplineAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , m_canvas(rhs.m_canvas) { } KisPaintingAssistantSP SplineAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new SplineAssistant(*this, handleMap)); } // parametric form of a cubic spline (B(t) = (1-t)^3 P0 + 3 (1-t)^2 t P1 + 3 (1-t) t^2 P2 + t^3 P3) inline QPointF B(qreal t, const QPointF& P0, const QPointF& P1, const QPointF& P2, const QPointF& P3) { const qreal tp = 1 - t; const qreal tp2 = tp * tp; const qreal t2 = t * t; return ( tp2 * tp) * P0 + (3 * tp2 * t ) * P1 + (3 * tp * t2) * P2 + ( t * t2) * P3; } // squared distance from a point on the spline to given point: we want to minimize this inline qreal D(qreal t, const QPointF& P0, const QPointF& P1, const QPointF& P2, const QPointF& P3, const QPointF& p) { const qreal tp = 1 - t, tp2 = tp * tp, t2 = t * t, a = tp2 * tp, b = 3 * tp2 * t, c = 3 * tp * t2, d = t * t2, x_dist = a*P0.x() + b*P1.x() + c*P2.x() + d*P3.x() - p.x(), y_dist = a*P0.y() + b*P1.y() + c*P2.y() + d*P3.y() - p.y(); return x_dist * x_dist + y_dist * y_dist; } QPointF SplineAssistant::project(const QPointF& pt) const { Q_ASSERT(isAssistantComplete()); // minimize d(t), but keep t in the same neighbourhood as before (unless starting a new stroke) // (this is a rather inefficient method) qreal min_t = std::numeric_limits::max(); qreal d_min_t = std::numeric_limits::max(); for (qreal t = 0; t <= 1; t += 1e-3) { qreal d_t = D(t, *handles()[0], *handles()[2], *handles()[3], *handles()[1], pt); if (d_t < d_min_t) { d_min_t = d_t; min_t = t; } } return B(min_t, *handles()[0], *handles()[2], *handles()[3], *handles()[1]); } QPointF SplineAssistant::adjustPosition(const QPointF& pt, const QPointF& /*strokeBegin*/) { return project(pt); } void SplineAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible) { gc.save(); gc.resetTransform(); QPoint mousePos; if (canvas){ //simplest, cheapest way to get the mouse-position// mousePos= canvas->canvasWidget()->mapFromGlobal(QCursor::pos()); m_canvas = canvas; } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in spline, you may have passed arguments incorrectly:"< 1) { QTransform initialTransform = converter->documentToWidgetTransform(); // first we find the path that our point create. QPointF pts[4]; pts[0] = *handles()[0]; pts[1] = *handles()[1]; pts[2] = (handles().size() >= 3) ? (*handles()[2]) : (*handles()[0]); pts[3] = (handles().size() >= 4) ? (*handles()[3]) : (handles().size() >= 3) ? (*handles()[2]) : (*handles()[1]); gc.setTransform(initialTransform); // Draw the spline QPainterPath path; path.moveTo(pts[0]); path.cubicTo(pts[2], pts[3], pts[1]); //then we use this path to check the bounding rectangle// if (isSnappingActive() && path.boundingRect().contains(initialTransform.inverted().map(mousePos)) && previewVisible==true){ drawPreview(gc, path);//and we draw the preview. } } gc.restore(); // there is some odd rectangle that is getting rendered when there is only one point, so don't start rendering the line until after 2 // this issue only exists with this spline assistant...none of the others if (handles().size() > 2) { KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } } void SplineAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (assistantVisible == false || handles().size() < 2 ){ return; } QTransform initialTransform = converter->documentToWidgetTransform(); QPointF pts[4]; pts[0] = *handles()[0]; pts[1] = *handles()[1]; pts[2] = (handles().size() >= 3) ? (*handles()[2]) : (*handles()[0]); pts[3] = (handles().size() >= 4) ? (*handles()[3]) : (handles().size() >= 3) ? (*handles()[2]) : (*handles()[1]); gc.setTransform(initialTransform); { // Draw bezier handles control lines only if we are editing the assistant gc.save(); QColor assistantColor = effectiveAssistantColor(); QPen bezierlinePen(assistantColor); bezierlinePen.setStyle(Qt::DotLine); bezierlinePen.setWidth(1); if (m_canvas->paintingAssistantsDecoration()->isEditingAssistants()) { if (!isSnappingActive()) { QColor snappingColor = assistantColor; snappingColor.setAlpha(snappingColor.alpha() * 0.2); bezierlinePen.setColor(snappingColor); } gc.setPen(bezierlinePen); gc.drawLine(pts[0], pts[2]); if (isAssistantComplete()) { gc.drawLine(pts[1], pts[3]); } gc.setPen(QColor(0, 0, 0, 125)); } gc.restore(); } // Draw the spline QPainterPath path; path.moveTo(pts[0]); path.cubicTo(pts[2], pts[3], pts[1]); drawPath(gc, path, isSnappingActive()); } QPointF SplineAssistant::buttonPosition() const { return B(0.5, *handles()[0], *handles()[2], *handles()[3], *handles()[1]); } bool SplineAssistant::isAssistantComplete() const { return handles().size() >= 4; // specify 4 corners to make assistant complete } SplineAssistantFactory::SplineAssistantFactory() { } SplineAssistantFactory::~SplineAssistantFactory() { } QString SplineAssistantFactory::id() const { return "spline"; } QString SplineAssistantFactory::name() const { return i18n("Spline"); } KisPaintingAssistant* SplineAssistantFactory::createPaintingAssistant() const { return new SplineAssistant; } diff --git a/plugins/assistants/Assistants/SplineAssistant.h b/plugins/assistants/Assistants/SplineAssistant.h index bda76ac790..0084a3be80 100644 --- a/plugins/assistants/Assistants/SplineAssistant.h +++ b/plugins/assistants/Assistants/SplineAssistant.h @@ -1,57 +1,58 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SPLINE_ASSISTANT_H_ #define _SPLINE_ASSISTANT_H_ #include "kis_painting_assistant.h" #include class SplineAssistant : public KisPaintingAssistant { public: SplineAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; QPointF buttonPosition() const override; int numHandles() const override { return 4; } bool isAssistantComplete() const override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt) const; explicit SplineAssistant(const SplineAssistant &rhs, QMap &handleMap); /// used for getting the decoration so the bezier handles aren't drawn while editing KisCanvas2* m_canvas; }; class SplineAssistantFactory : public KisPaintingAssistantFactory { public: SplineAssistantFactory(); ~SplineAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/VanishingPointAssistant.cc b/plugins/assistants/Assistants/VanishingPointAssistant.cc index 31f91b9637..d3af88d14c 100644 --- a/plugins/assistants/Assistants/VanishingPointAssistant.cc +++ b/plugins/assistants/Assistants/VanishingPointAssistant.cc @@ -1,317 +1,318 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "VanishingPointAssistant.h" #include "kis_debug.h" #include #include #include #include #include #include #include #include #include VanishingPointAssistant::VanishingPointAssistant() : KisPaintingAssistant("vanishing point", i18n("Vanishing Point assistant")) { } VanishingPointAssistant::VanishingPointAssistant(const VanishingPointAssistant &rhs, QMap &handleMap) : KisPaintingAssistant(rhs, handleMap) , m_canvas(rhs.m_canvas) , m_referenceLineDensity(rhs.m_referenceLineDensity) { } KisPaintingAssistantSP VanishingPointAssistant::clone(QMap &handleMap) const { return KisPaintingAssistantSP(new VanishingPointAssistant(*this, handleMap)); } QPointF VanishingPointAssistant::project(const QPointF& pt, const QPointF& strokeBegin) { //Q_ASSERT(handles().size() == 1 || handles().size() == 5); //code nicked from the perspective ruler. qreal dx = pt.x() - strokeBegin.x(); qreal dy = pt.y() - strokeBegin.y(); if (dx * dx + dy * dy < 4.0) { // allow some movement before snapping return strokeBegin; } //dbgKrita<canvasWidget()->mapFromGlobal(QCursor::pos()); m_canvas = canvas; } else { //...of course, you need to have access to a canvas-widget for that.// mousePos = QCursor::pos();//this'll give an offset// dbgFile<<"canvas does not exist in ruler, you may have passed arguments incorrectly:"<paintingAssistantsDecoration()->isEditingAssistants() == false && isAssistantComplete()) { if (isSnappingActive() && previewVisible == true) { //don't draw if invalid. QTransform initialTransform = converter->documentToWidgetTransform(); QPointF startPoint = initialTransform.map(*handles()[0]); QLineF snapLine= QLineF(startPoint, mousePos); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); QRect bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint()); QPainterPath path; if (bounds.contains(startPoint.toPoint())){ path.moveTo(startPoint); path.lineTo(snapLine.p1()); } else { path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); } drawPreview(gc, path);//and we draw the preview. } } // editor specific controls display if (canvas->paintingAssistantsDecoration()->isEditingAssistants()) { // draws a circle around the vanishing point node while editing QTransform initialTransform = converter->documentToWidgetTransform(); QPointF p0 = initialTransform.map(*handles()[0]); // main vanishing point QPointF p1 = initialTransform.map(*sideHandles()[0]); QPointF p2 = initialTransform.map(*sideHandles()[1]); QPointF p3 = initialTransform.map(*sideHandles()[2]); QPointF p4 = initialTransform.map(*sideHandles()[3]); QRectF ellipse = QRectF(QPointF(p0.x() -15, p0.y() -15), QSizeF(30, 30)); QPainterPath pathCenter; pathCenter.addEllipse(ellipse); drawPath(gc, pathCenter, isSnappingActive()); QColor paintingColor = effectiveAssistantColor(); // draw the lines connecting the different nodes QPen penStyle(paintingColor, 2.0, Qt::SolidLine); if (!isSnappingActive()) { QColor snappingColor = paintingColor; snappingColor.setAlpha(snappingColor.alpha() * 0.2); penStyle.setColor(snappingColor); } gc.save(); gc.setPen(penStyle); gc.drawLine(p0, p1); gc.drawLine(p0, p3); gc.drawLine(p1, p2); gc.drawLine(p3, p4); gc.restore(); } // draw references guide for vanishing points at specified density // this is shown as part of the preview, so don't show if preview is off if( canvas->paintingAssistantsDecoration()->outlineVisibility() && this->isSnappingActive() ) { // cycle through degrees from 0 to 180. We are doing an infinite line, so we don't need to go 360 QTransform initialTransform = converter->documentToWidgetTransform(); QPointF p0 = initialTransform.map(*handles()[0]); // main vanishing point for (int currentAngle=0; currentAngle <= 180; currentAngle = currentAngle + m_referenceLineDensity ) { // determine the correct angle based on the iteration float xPos = cos(currentAngle * M_PI / 180); float yPos = sin(currentAngle * M_PI / 180); QPointF unitAngle; unitAngle.setX(p0.x() + xPos); unitAngle.setY(p0.y() + yPos); // find point QLineF snapLine= QLineF(p0, unitAngle); QRect viewport= gc.viewport(); KisAlgebra2D::intersectLineRect(snapLine, viewport); // make a line from VP center to edge of canvas with that angle QPainterPath path; path.moveTo(snapLine.p1()); path.lineTo(snapLine.p2()); drawPreview(gc, path);//and we draw the preview. } } gc.restore(); KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible); } void VanishingPointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible) { if (!m_canvas || !isAssistantComplete()) { return; } if (assistantVisible == false || m_canvas->paintingAssistantsDecoration()->isEditingAssistants()) { return; } QTransform initialTransform = converter->documentToWidgetTransform(); QPointF p0 = initialTransform.map(*handles()[0]); // draws an "X" QPainterPath path; path.moveTo(QPointF(p0.x() - 10.0, p0.y() - 10.0)); path.lineTo(QPointF(p0.x() + 10.0, p0.y() + 10.0)); path.moveTo(QPointF(p0.x() - 10.0, p0.y() + 10.0)); path.lineTo(QPointF(p0.x() + 10.0, p0.y() - 10.0)); drawPath(gc, path, isSnappingActive()); } QPointF VanishingPointAssistant::buttonPosition() const { return (*handles()[0]); } void VanishingPointAssistant::setReferenceLineDensity(float value) { // cannot have less than 1 degree value if (value < 1.0) { value = 1.0; } m_referenceLineDensity = value; } float VanishingPointAssistant::referenceLineDensity() { return m_referenceLineDensity; } bool VanishingPointAssistant::isAssistantComplete() const { return handles().size() > 0; // only need one point to be ready } void VanishingPointAssistant::saveCustomXml(QXmlStreamWriter* xml) { xml->writeStartElement("angleDensity"); xml->writeAttribute("value", KisDomUtils::toString( this->referenceLineDensity())); xml->writeEndElement(); } bool VanishingPointAssistant::loadCustomXml(QXmlStreamReader* xml) { if (xml && xml->name() == "angleDensity") { this->setReferenceLineDensity((float)KisDomUtils::toDouble(xml->attributes().value("value").toString())); } return true; } VanishingPointAssistantFactory::VanishingPointAssistantFactory() { } VanishingPointAssistantFactory::~VanishingPointAssistantFactory() { } QString VanishingPointAssistantFactory::id() const { return "vanishing point"; } QString VanishingPointAssistantFactory::name() const { return i18n("Vanishing Point"); } KisPaintingAssistant* VanishingPointAssistantFactory::createPaintingAssistant() const { return new VanishingPointAssistant; } diff --git a/plugins/assistants/Assistants/VanishingPointAssistant.h b/plugins/assistants/Assistants/VanishingPointAssistant.h index 48e2802aa1..3710e602bf 100644 --- a/plugins/assistants/Assistants/VanishingPointAssistant.h +++ b/plugins/assistants/Assistants/VanishingPointAssistant.h @@ -1,84 +1,85 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2014 Wolthera van Hövell tot Westerflier * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _VANISHINGPOINT_ASSISTANT_H_ #define _VANISHINGPOINT_ASSISTANT_H_ #include "kis_painting_assistant.h" #include #include #include #include /* Design: *The idea behind the vanishing point ruler is that in a perspective deformed landscape, a set of parallel *lines al share a single vanishing point. *Therefore, a perspective can contain an theoretical infinite of vanishing points. *It's a pity if we only allowed an artist to access 1, 2 or 3 of these at any given time, as other *solutions for perspective tools do. *Hence a vanishing point ruler. * *This ruler is relatively simple compared to the other perspective ruler: *It has only one vanishing point that is required to draw. *However, it does have it's own weaknesses in how to determine onto which of these infinite rulers to snap. *Furthermore, it has four extra handles for adding a perspective ruler to a preexisting perspective. */ //class VanishingPoint; class VanishingPointAssistant : public KisPaintingAssistant { public: VanishingPointAssistant(); KisPaintingAssistantSP clone(QMap &handleMap) const override; QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) override; //virtual void endStroke(); QPointF buttonPosition() const override; int numHandles() const override { return 1; } float referenceLineDensity(); void setReferenceLineDensity(float value); bool isAssistantComplete() const override; void saveCustomXml(QXmlStreamWriter* xml) override; bool loadCustomXml(QXmlStreamReader* xml) override; protected: void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached = true,KisCanvas2* canvas=0, bool assistantVisible=true, bool previewVisible=true) override; void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override; private: QPointF project(const QPointF& pt, const QPointF& strokeBegin); explicit VanishingPointAssistant(const VanishingPointAssistant &rhs, QMap &handleMap); KisCanvas2 *m_canvas; float m_referenceLineDensity = 15.0; }; class VanishingPointAssistantFactory : public KisPaintingAssistantFactory { public: VanishingPointAssistantFactory(); ~VanishingPointAssistantFactory() override; QString id() const override; QString name() const override; KisPaintingAssistant* createPaintingAssistant() const override; }; #endif diff --git a/plugins/assistants/Assistants/assistant_tool.cc b/plugins/assistants/Assistants/assistant_tool.cc index 430421d45b..69d87c8ffe 100644 --- a/plugins/assistants/Assistants/assistant_tool.cc +++ b/plugins/assistants/Assistants/assistant_tool.cc @@ -1,60 +1,61 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "assistant_tool.h" #include "kis_assistant_tool.h" #include #include #include #include #include #include "RulerAssistant.h" #include "EllipseAssistant.h" #include "SplineAssistant.h" #include "PerspectiveAssistant.h" #include "VanishingPointAssistant.h" #include "InfiniteRulerAssistant.h" #include "ParallelRulerAssistant.h" #include "ConcentricEllipseAssistant.h" #include "FisheyePointAssistant.h" //#include "mesh_assistant.h" K_PLUGIN_FACTORY_WITH_JSON(AssistantToolFactory, "kritaassistanttool.json", registerPlugin();) AssistantToolPlugin::AssistantToolPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoToolRegistry::instance()->add(new KisAssistantToolFactory()); KisPaintingAssistantFactoryRegistry::instance()->add(new RulerAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new EllipseAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new SplineAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new PerspectiveAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new VanishingPointAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new InfiniteRulerAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new ParallelRulerAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new ConcentricEllipseAssistantFactory); KisPaintingAssistantFactoryRegistry::instance()->add(new FisheyePointAssistantFactory); // KisPaintingAssistantFactoryRegistry::instance()->add(new MeshAssistantFactory); } AssistantToolPlugin::~AssistantToolPlugin() { } #include "assistant_tool.moc" diff --git a/plugins/assistants/Assistants/assistant_tool.h b/plugins/assistants/Assistants/assistant_tool.h index ef76fb0925..739f2a8ce2 100644 --- a/plugins/assistants/Assistants/assistant_tool.h +++ b/plugins/assistants/Assistants/assistant_tool.h @@ -1,34 +1,35 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _RULERASSISTANTTOOL_H_ #define _RULERASSISTANTTOOL_H_ #include #include class AssistantToolPlugin : public QObject { Q_OBJECT public: AssistantToolPlugin(QObject *parent, const QVariantList &); ~AssistantToolPlugin() override; }; #endif diff --git a/plugins/assistants/Assistants/kis_assistant_tool.cc b/plugins/assistants/Assistants/kis_assistant_tool.cc index 7cd15bec72..e371b6a87b 100644 --- a/plugins/assistants/Assistants/kis_assistant_tool.cc +++ b/plugins/assistants/Assistants/kis_assistant_tool.cc @@ -1,1061 +1,1062 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Geoffry Song * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "VanishingPointAssistant.h" #include "EditAssistantsCommand.h" #include #include KisAssistantTool::KisAssistantTool(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::arrowCursor()), m_canvas(dynamic_cast(canvas)), m_assistantDrag(0), m_newAssistant(0), m_optionsWidget(0) { Q_ASSERT(m_canvas); setObjectName("tool_assistanttool"); } KisAssistantTool::~KisAssistantTool() { } void KisAssistantTool::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); m_canvas->paintingAssistantsDecoration()->activateAssistantsEditor(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_handleDrag = 0; m_internalMode = MODE_CREATION; m_assistantHelperYOffset = 10; m_handleSize = 17; m_canvas->paintingAssistantsDecoration()->setHandleSize(m_handleSize); if (m_optionsWidget) { m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } m_canvas->updateCanvas(); } void KisAssistantTool::deactivate() { m_canvas->paintingAssistantsDecoration()->deactivateAssistantsEditor(); m_canvas->updateCanvas(); KisTool::deactivate(); } void KisAssistantTool::beginPrimaryAction(KoPointerEvent *event) { setMode(KisTool::PAINT_MODE); m_origAssistantList = KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()); bool newAssistantAllowed = true; KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration(); if (m_newAssistant) { m_internalMode = MODE_CREATION; *m_newAssistant->handles().back() = canvasDecoration->snapToGuide(event, QPointF(), false); if (m_newAssistant->handles().size() == m_newAssistant->numHandles()) { addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); } m_canvas->updateCanvas(); return; } m_handleDrag = 0; double minDist = 81.0; QPointF mousePos = m_canvas->viewConverter()->documentToView(canvasDecoration->snapToGuide(event, QPointF(), false));//m_canvas->viewConverter()->documentToView(event->point); // syncs the assistant handles to the handles reference we store in this tool // they can get out of sync with the way the actions and paintevents occur // we probably need to stop storing a reference in m_handles and call the assistants directly m_handles = m_canvas->paintingAssistantsDecoration()->handles(); Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { // find out which handle on all assistants is closest to the mouse position // vanishing points have "side handles", so make sure to include that { QList allAssistantHandles; allAssistantHandles.append(assistant->handles()); allAssistantHandles.append(assistant->sideHandles()); Q_FOREACH (const KisPaintingAssistantHandleSP handle, allAssistantHandles) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleDrag = handle; assistantSelected(assistant); // whatever handle is the closest contains the selected assistant } } } if(m_handleDrag && assistant->id() == "perspective") { // Look for the handle which was pressed if (m_handleDrag == assistant->topLeft()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_dragStart = QPointF(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_internalMode = MODE_DRAGGING_NODE; } else if (m_handleDrag == assistant->topRight()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); } else if (m_handleDrag == assistant->bottomLeft()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); } else if (m_handleDrag == assistant->bottomRight()) { double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag)); if (dist < minDist) { minDist = dist; } m_internalMode = MODE_DRAGGING_NODE; m_dragStart = QPointF(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); } else if (m_handleDrag == assistant->leftMiddle()) { m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->topLeft().data()->x())*0.5, (assistant->bottomLeft().data()->y()+assistant->topLeft().data()->y())*0.5); m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->topLeft(), HandleType::NORMAL ); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomLeft(), HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->rightMiddle()) { m_dragStart = QPointF((assistant->topRight().data()->x()+assistant->bottomRight().data()->x())*0.5, (assistant->topRight().data()->y()+assistant->bottomRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->topRight(), HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->topMiddle()) { m_dragStart = QPointF((assistant->topLeft().data()->x()+assistant->topRight().data()->x())*0.5, (assistant->topLeft().data()->y()+assistant->topRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(assistant->topRight(), HandleType::NORMAL); m_newAssistant->addHandle(assistant->topLeft(), HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } else if (m_handleDrag == assistant->bottomMiddle()) { m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->bottomRight().data()->x())*0.5, (assistant->bottomLeft().data()->y()+assistant->bottomRight().data()->y())*0.5); m_internalMode = MODE_DRAGGING_TRANSLATING_TWONODES; m_selectedNode1 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y()); m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y()); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant()); m_newAssistant->addHandle(assistant->bottomLeft(), HandleType::NORMAL); m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode2, HandleType::NORMAL); m_newAssistant->addHandle(m_selectedNode1, HandleType::NORMAL); m_dragEnd = event->point; m_handleDrag = 0; m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } m_snapIsRadial = false; } else if (m_handleDrag && assistant->handles().size()>1 && (assistant->id() == "ruler" || assistant->id() == "parallel ruler" || assistant->id() == "infinite ruler" || assistant->id() == "spline")){ if (m_handleDrag == assistant->handles()[0]) { m_dragStart = *assistant->handles()[1]; } else if (m_handleDrag == assistant->handles()[1]) { m_dragStart = *assistant->handles()[0]; } else if(assistant->handles().size()==4){ if (m_handleDrag == assistant->handles()[2]) { m_dragStart = *assistant->handles()[0]; } else if (m_handleDrag == assistant->handles()[3]) { m_dragStart = *assistant->handles()[1]; } } m_snapIsRadial = false; } else if (m_handleDrag && assistant->handles().size()>2 && (assistant->id() == "ellipse" || assistant->id() == "concentric ellipse" || assistant->id() == "fisheye-point")){ m_snapIsRadial = false; if (m_handleDrag == assistant->handles()[0]) { m_dragStart = *assistant->handles()[1]; } else if (m_handleDrag == assistant->handles()[1]) { m_dragStart = *assistant->handles()[0]; } else if (m_handleDrag == assistant->handles()[2]) { m_dragStart = assistant->buttonPosition(); m_radius = QLineF(m_dragStart, *assistant->handles()[0]); m_snapIsRadial = true; } } else { m_dragStart = assistant->buttonPosition(); m_snapIsRadial = false; } } if (m_handleDrag) { // TODO: Shift-press should now be handled using the alternate actions // if (event->modifiers() & Qt::ShiftModifier) { // m_handleDrag->uncache(); // m_handleDrag = m_handleDrag->split()[0]; // m_handles = m_canvas->view()->paintingAssistantsDecoration()->handles(); // } m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas return; } m_assistantDrag.clear(); Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { // This code contains the click event behavior. QTransform initialTransform = m_canvas->coordinatesConverter()->documentToWidgetTransform(); QPointF actionsPosition = initialTransform.map(assistant->buttonPosition()); // for UI editor widget controls with move, show, and delete -- disregard document transforms like rotating and mirroring. // otherwise the UI controls get awkward to use when they are at 45 degree angles or the order of controls gets flipped backwards QPointF uiMousePosition = initialTransform.map( canvasDecoration->snapToGuide(event, QPointF(), false)); AssistantEditorData editorShared; // shared position data between assistant tool and decoration QPointF iconMovePosition(actionsPosition + editorShared.moveIconPosition); QPointF iconSnapPosition(actionsPosition + editorShared.snapIconPosition); QPointF iconDeletePosition(actionsPosition + editorShared.deleteIconPosition); QRectF deleteRect(iconDeletePosition, QSizeF(editorShared.deleteIconSize, editorShared.deleteIconSize)); QRectF visibleRect(iconSnapPosition, QSizeF(editorShared.snapIconSize, editorShared.snapIconSize)); QRectF moveRect(iconMovePosition, QSizeF(editorShared.moveIconSize, editorShared.moveIconSize)); if (moveRect.contains(uiMousePosition)) { m_assistantDrag = assistant; m_cursorStart = event->point; m_currentAdjustment = QPointF(); m_internalMode = MODE_EDITING; assistantSelected(assistant); // whatever handle is the closest contains the selected assistant return; } if (deleteRect.contains(uiMousePosition)) { removeAssistant(assistant); if(m_canvas->paintingAssistantsDecoration()->assistants().isEmpty()) { m_internalMode = MODE_CREATION; } else m_internalMode = MODE_EDITING; m_canvas->updateCanvas(); return; } if (visibleRect.contains(uiMousePosition)) { newAssistantAllowed = false; assistant->setSnappingActive(!assistant->isSnappingActive()); // toggle assistant->uncache();//this updates the chache of the assistant, very important. assistantSelected(assistant); // whatever handle is the closest contains the selected assistant } } if (newAssistantAllowed==true){//don't make a new assistant when I'm just toogling visibility// QString key = m_options.availableAssistantsComboBox->model()->index( m_options.availableAssistantsComboBox->currentIndex(), 0 ).data(Qt::UserRole).toString(); m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get(key)->createPaintingAssistant()); m_internalMode = MODE_CREATION; m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); if (m_newAssistant->numHandles() <= 1) { addAssistant(); } else { m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL); } } if (m_newAssistant) { m_newAssistant->setAssistantGlobalColorCache(m_canvas->paintingAssistantsDecoration()->globalAssistantsColor()); } m_canvas->updateCanvas(); } void KisAssistantTool::continuePrimaryAction(KoPointerEvent *event) { KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration(); if (m_handleDrag) { *m_handleDrag = event->point; //ported from the gradient tool... we need to think about this more in the future. if (event->modifiers() == Qt::ShiftModifier && m_snapIsRadial) { QLineF dragRadius = QLineF(m_dragStart, event->point); dragRadius.setLength(m_radius.length()); *m_handleDrag = dragRadius.p2(); } else if (event->modifiers() == Qt::ShiftModifier ) { QPointF move = snapToClosestAxis(event->point - m_dragStart); *m_handleDrag = m_dragStart + move; } else { *m_handleDrag = canvasDecoration->snapToGuide(event, QPointF(), false); } m_handleDrag->uncache(); m_handleCombine = 0; if (!(event->modifiers() & Qt::ShiftModifier)) { double minDist = 49.0; QPointF mousePos = m_canvas->viewConverter()->documentToView(event->point); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { if (handle == m_handleDrag) continue; double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle)); if (dist < minDist) { minDist = dist; m_handleCombine = handle; } } } m_canvas->updateCanvas(); } else if (m_assistantDrag) { QPointF newAdjustment = canvasDecoration->snapToGuide(event, QPointF(), false) - m_cursorStart; if (event->modifiers() == Qt::ShiftModifier ) { newAdjustment = snapToClosestAxis(newAdjustment); } Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->handles()) { *handle += (newAdjustment - m_currentAdjustment); } if (m_assistantDrag->id()== "vanishing point"){ Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->sideHandles()) { *handle += (newAdjustment - m_currentAdjustment); } } m_currentAdjustment = newAdjustment; m_canvas->updateCanvas(); } else { event->ignore(); } bool wasHiglightedNode = m_higlightedNode != 0; QPointF mousep = m_canvas->viewConverter()->documentToView(event->point); QList pAssistant= m_canvas->paintingAssistantsDecoration()->assistants(); Q_FOREACH (KisPaintingAssistantSP assistant, pAssistant) { if(assistant->id() == "perspective") { if ((m_higlightedNode = assistant->closestCornerHandleFromPoint(mousep))) { if (m_higlightedNode == m_selectedNode1 || m_higlightedNode == m_selectedNode2) { m_higlightedNode = 0; } else { m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas break; } } } //this following bit sets the translations for the vanishing-point handles. if(m_handleDrag && assistant->id() == "vanishing point" && assistant->sideHandles().size()==4) { //for inner handles, the outer handle gets translated. if (m_handleDrag == assistant->sideHandles()[0]) { QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[0]); qreal length = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]).length(); if (length < 2.0){ length = 2.0; } length += perspectiveline.length(); perspectiveline.setLength(length); *assistant->sideHandles()[1] = perspectiveline.p2(); } else if (m_handleDrag == assistant->sideHandles()[2]){ QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]); qreal length = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length(); if (length<2.0){ length=2.0; } length += perspectiveline.length(); perspectiveline.setLength(length); *assistant->sideHandles()[3] = perspectiveline.p2(); } // for outer handles, only the vanishing point is translated, but only if there's an intersection. else if (m_handleDrag == assistant->sideHandles()[1]|| m_handleDrag == assistant->sideHandles()[3]){ QPointF vanishingpoint(0,0); QLineF perspectiveline = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]); QLineF perspectiveline2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]); if (QLineF(perspectiveline2).intersect(QLineF(perspectiveline), &vanishingpoint) != QLineF::NoIntersection){ *assistant->handles()[0] = vanishingpoint; } }// and for the vanishing point itself, only the outer handles get translated. else if (m_handleDrag == assistant->handles()[0]){ QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[0]); QLineF perspectiveline2 = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]); qreal length = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]).length(); qreal length2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length(); if (length < 2.0) { length = 2.0; } if (length2 < 2.0) { length2=2.0; } length += perspectiveline.length(); length2 += perspectiveline2.length(); perspectiveline.setLength(length); perspectiveline2.setLength(length2); *assistant->sideHandles()[1] = perspectiveline.p2(); *assistant->sideHandles()[3] = perspectiveline2.p2(); } } } if (wasHiglightedNode && !m_higlightedNode) { m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } } void KisAssistantTool::endPrimaryAction(KoPointerEvent *event) { setMode(KisTool::HOVER_MODE); if (m_handleDrag || m_assistantDrag) { if (m_handleDrag) { if (!(event->modifiers() & Qt::ShiftModifier) && m_handleCombine) { m_handleCombine->mergeWith(m_handleDrag); m_handleCombine->uncache(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); } m_handleDrag = m_handleCombine = 0; } else { m_assistantDrag.clear(); } dbgUI << "creating undo command..."; KUndo2Command *command = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants())); m_canvas->viewManager()->undoAdapter()->addCommand(command); dbgUI << "done"; } else if(m_internalMode == MODE_DRAGGING_TRANSLATING_TWONODES) { addAssistant(); m_internalMode = MODE_CREATION; } else { event->ignore(); } m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas } void KisAssistantTool::addAssistant() { m_canvas->paintingAssistantsDecoration()->addAssistant(m_newAssistant); KisAbstractPerspectiveGrid* grid = dynamic_cast(m_newAssistant.data()); if (grid) { m_canvas->viewManager()->canvasResourceProvider()->addPerspectiveGrid(grid); } QList assistants = m_canvas->paintingAssistantsDecoration()->assistants(); KUndo2Command *addAssistantCmd = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(assistants), EditAssistantsCommand::ADD, assistants.indexOf(m_newAssistant)); m_canvas->viewManager()->undoAdapter()->addCommand(addAssistantCmd); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(m_newAssistant); updateToolOptionsUI(); // vanishing point assistant will get an extra option m_newAssistant.clear(); } void KisAssistantTool::removeAssistant(KisPaintingAssistantSP assistant) { QList assistants = m_canvas->paintingAssistantsDecoration()->assistants(); KisAbstractPerspectiveGrid* grid = dynamic_cast(assistant.data()); if (grid) { m_canvas->viewManager()->canvasResourceProvider()->removePerspectiveGrid(grid); } m_canvas->paintingAssistantsDecoration()->removeAssistant(assistant); KUndo2Command *removeAssistantCmd = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()), EditAssistantsCommand::REMOVE, assistants.indexOf(assistant)); m_canvas->viewManager()->undoAdapter()->addCommand(removeAssistantCmd); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } void KisAssistantTool::assistantSelected(KisPaintingAssistantSP assistant) { m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(assistant); updateToolOptionsUI(); } void KisAssistantTool::updateToolOptionsUI() { KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); bool hasActiveAssistant = m_selectedAssistant ? true : false; if (m_selectedAssistant) { bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; m_options.vanishingPointAngleSpinbox->setVisible(isVanishingPointAssistant); if (isVanishingPointAssistant) { QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); m_options.vanishingPointAngleSpinbox->setValue(assis->referenceLineDensity()); } // load custom color settings from assistant (this happens when changing assistant m_options.useCustomAssistantColor->setChecked(m_selectedAssistant->useCustomColor()); m_options.customAssistantColorButton->setColor(m_selectedAssistant->assistantCustomColor()); float opacity = (float)m_selectedAssistant->assistantCustomColor().alpha()/255.0 * 100.0 ; m_options.customColorOpacitySlider->setValue(opacity); } else { m_options.vanishingPointAngleSpinbox->setVisible(false); // } // show/hide elements if an assistant is selected or not m_options.assistantsGlobalOpacitySlider->setVisible(hasActiveAssistant); m_options.assistantsColor->setVisible(hasActiveAssistant); m_options.globalColorLabel->setVisible(hasActiveAssistant); m_options.useCustomAssistantColor->setVisible(hasActiveAssistant); // hide custom color options if use custom color is not selected bool showCustomColorSettings = m_options.useCustomAssistantColor->isChecked() && hasActiveAssistant; m_options.customColorOpacitySlider->setVisible(showCustomColorSettings); m_options.customAssistantColorButton->setVisible(showCustomColorSettings); // disable global color settings if we are using the custom color m_options.assistantsGlobalOpacitySlider->setEnabled(!showCustomColorSettings); m_options.assistantsColor->setEnabled(!showCustomColorSettings); m_options.globalColorLabel->setEnabled(!showCustomColorSettings); } void KisAssistantTool::slotChangeVanishingPointAngle(double value) { if ( m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) { return; } // get the selected assistant and change the angle value KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point"; if (isVanishingPointAssistant) { QSharedPointer assis = qSharedPointerCast(m_selectedAssistant); assis->setReferenceLineDensity((float)value); } } m_canvas->canvasWidget()->update(); } void KisAssistantTool::mouseMoveEvent(KoPointerEvent *event) { if (m_newAssistant && m_internalMode == MODE_CREATION) { *m_newAssistant->handles().back() = event->point; } else if (m_newAssistant && m_internalMode == MODE_DRAGGING_TRANSLATING_TWONODES) { QPointF translate = event->point - m_dragEnd; m_dragEnd = event->point; m_selectedNode1.data()->operator = (QPointF(m_selectedNode1.data()->x(),m_selectedNode1.data()->y()) + translate); m_selectedNode2.data()->operator = (QPointF(m_selectedNode2.data()->x(),m_selectedNode2.data()->y()) + translate); } m_canvas->updateCanvas(); } void KisAssistantTool::paint(QPainter& _gc, const KoViewConverter &_converter) { QRectF canvasSize = QRectF(QPointF(0, 0), QSizeF(m_canvas->image()->size())); // show special display while a new assistant is in the process of being created if (m_newAssistant) { QColor assistantColor = m_newAssistant->effectiveAssistantColor(); assistantColor.setAlpha(80); m_newAssistant->drawAssistant(_gc, canvasSize, m_canvas->coordinatesConverter(), false, m_canvas, true, false); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_newAssistant->handles()) { QPainterPath path; path.addEllipse(QRectF(_converter.documentToView(*handle) - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize))); _gc.save(); _gc.setPen(Qt::NoPen); _gc.setBrush(assistantColor); _gc.drawPath(path); _gc.restore(); } } Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { QColor assistantColor = assistant->effectiveAssistantColor(); assistantColor.setAlpha(80); Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { QRectF ellipse(_converter.documentToView(*handle) - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize)); // render handles differently if it is the one being dragged. if (handle == m_handleDrag || handle == m_handleCombine) { QPen stroke(assistantColor, 4); _gc.save(); _gc.setPen(stroke); _gc.setBrush(Qt::NoBrush); _gc.drawEllipse(ellipse); _gc.restore(); } } } } void KisAssistantTool::removeAllAssistants() { m_canvas->viewManager()->canvasResourceProvider()->clearPerspectiveGrids(); m_canvas->paintingAssistantsDecoration()->removeAll(); m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); m_canvas->paintingAssistantsDecoration()->deselectAssistant(); updateToolOptionsUI(); } void KisAssistantTool::loadAssistants() { KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenAssistant"); dialog.setCaption(i18n("Select an Assistant")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant"); QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; QFile file(filename); file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); QXmlStreamReader xml(data); QMap handleMap; KisPaintingAssistantSP assistant; bool errors = false; while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name() == "handle") { if (assistant && !xml.attributes().value("ref").isEmpty()) { KisPaintingAssistantHandleSP handle = handleMap.value(xml.attributes().value("ref").toString().toInt()); if (handle) { assistant->addHandle(handle, HandleType::NORMAL); } else { errors = true; } } else { QString strId = xml.attributes().value("id").toString(), strX = xml.attributes().value("x").toString(), strY = xml.attributes().value("y").toString(); if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) { int id = strId.toInt(); double x = strX.toDouble(), y = strY.toDouble(); if (!handleMap.contains(id)) { handleMap.insert(id, new KisPaintingAssistantHandle(x, y)); } else { errors = true; } } else { errors = true; } } } else if (xml.name() == "assistant") { const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(xml.attributes().value("type").toString()); if (factory) { if (assistant) { errors = true; assistant.clear(); } assistant = toQShared(factory->createPaintingAssistant()); } else { errors = true; } // load custom shared assistant properties if ( xml.attributes().hasAttribute("useCustomColor")) { QStringRef useCustomColor = xml.attributes().value("useCustomColor"); bool usingColor = false; if (useCustomColor.toString() == "1") { usingColor = true; } assistant->setUseCustomColor(usingColor); } if ( xml.attributes().hasAttribute("useCustomColor")) { QStringRef customColor = xml.attributes().value("customColor"); assistant->setAssistantCustomColor( KisDomUtils::qStringToQColor(customColor.toString()) ); } } if (assistant) { assistant->loadCustomXml(&xml); } break; case QXmlStreamReader::EndElement: if (xml.name() == "assistant") { if (assistant) { if (assistant->handles().size() == assistant->numHandles()) { if (assistant->id() == "vanishing point"){ //ideally we'd save and load side-handles as well, but this is all I've got// QPointF pos = *assistant->handles()[0]; assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-70,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-140,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(70,0)), HandleType::SIDE); assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(140,0)), HandleType::SIDE); } m_canvas->paintingAssistantsDecoration()->addAssistant(assistant); KisAbstractPerspectiveGrid* grid = dynamic_cast(assistant.data()); if (grid) { m_canvas->viewManager()->canvasResourceProvider()->addPerspectiveGrid(grid); } } else { errors = true; } assistant.clear(); } } break; default: break; } } if (assistant) { errors = true; assistant.clear(); } if (xml.hasError()) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), xml.errorString()); } if (errors) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Errors were encountered. Not all assistants were successfully loaded.")); } m_handles = m_canvas->paintingAssistantsDecoration()->handles(); m_canvas->updateCanvas(); } void KisAssistantTool::saveAssistants() { if (m_handles.isEmpty()) return; QByteArray data; QXmlStreamWriter xml(&data); xml.writeStartDocument(); xml.writeStartElement("paintingassistant"); xml.writeAttribute("color", KisDomUtils::qColorToQString( m_canvas->paintingAssistantsDecoration()->globalAssistantsColor())); // global color if no custom color used xml.writeStartElement("handles"); QMap handleMap; Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) { int id = handleMap.size(); handleMap.insert(handle, id); xml.writeStartElement("handle"); //xml.writeAttribute("type", handle->handleType()); xml.writeAttribute("id", QString::number(id)); xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3)); xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3)); xml.writeEndElement(); } xml.writeEndElement(); xml.writeStartElement("assistants"); Q_FOREACH (const KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) { xml.writeStartElement("assistant"); xml.writeAttribute("type", assistant->id()); xml.writeAttribute("useCustomColor", QString::number(assistant->useCustomColor())); xml.writeAttribute("customColor", KisDomUtils::qColorToQString(assistant->assistantCustomColor())); // custom assistant properties like angle density on vanishing point assistant->saveCustomXml(&xml); // handle information xml.writeStartElement("handles"); Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) { xml.writeStartElement("handle"); xml.writeAttribute("ref", QString::number(handleMap.value(handle))); xml.writeEndElement(); } xml.writeEndElement(); xml.writeEndElement(); } xml.writeEndElement(); xml.writeEndElement(); xml.writeEndDocument(); KoFileDialog dialog(m_canvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "OpenAssistant"); dialog.setCaption(i18n("Save Assistant")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant"); QString filename = dialog.filename(); if (filename.isEmpty()) return; QFile file(filename); file.open(QIODevice::WriteOnly); file.write(data); } QWidget *KisAssistantTool::createOptionWidget() { if (!m_optionsWidget) { m_optionsWidget = new QWidget; m_options.setupUi(m_optionsWidget); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); m_options.loadAssistantButton->setIcon(KisIconUtils::loadIcon("document-open")); m_options.saveAssistantButton->setIcon(KisIconUtils::loadIcon("document-save")); m_options.deleteAllAssistantsButton->setIcon(KisIconUtils::loadIcon("edit-delete")); QList assistants; Q_FOREACH (const QString& key, KisPaintingAssistantFactoryRegistry::instance()->keys()) { QString name = KisPaintingAssistantFactoryRegistry::instance()->get(key)->name(); assistants << KoID(key, name); } std::sort(assistants.begin(), assistants.end(), KoID::compareNames); Q_FOREACH(const KoID &id, assistants) { m_options.availableAssistantsComboBox->addItem(id.name(), id.id()); } connect(m_options.saveAssistantButton, SIGNAL(clicked()), SLOT(saveAssistants())); connect(m_options.loadAssistantButton, SIGNAL(clicked()), SLOT(loadAssistants())); connect(m_options.deleteAllAssistantsButton, SIGNAL(clicked()), SLOT(removeAllAssistants())); connect(m_options.assistantsColor, SIGNAL(changed(QColor)), SLOT(slotGlobalAssistantsColorChanged(QColor))); connect(m_options.assistantsGlobalOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotGlobalAssistantOpacityChanged())); connect(m_options.vanishingPointAngleSpinbox, SIGNAL(valueChanged(double)), this, SLOT(slotChangeVanishingPointAngle(double))); //ENTER_FUNCTION() << ppVar(m_canvas) << ppVar(m_canvas && m_canvas->paintingAssistantsDecoration()); // initialize UI elements with existing data if possible if (m_canvas && m_canvas->paintingAssistantsDecoration()) { const QColor color = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor(); QColor opaqueColor = color; opaqueColor.setAlpha(255); //ENTER_FUNCTION() << ppVar(opaqueColor); m_options.assistantsColor->setColor(opaqueColor); m_options.customAssistantColorButton->setColor(opaqueColor); m_options.assistantsGlobalOpacitySlider->setValue(color.alphaF() * 100.0); } else { m_options.assistantsColor->setColor(QColor(176, 176, 176, 255)); // grey default for all assistants m_options.assistantsGlobalOpacitySlider->setValue(100); // 100% } m_options.assistantsGlobalOpacitySlider->setPrefix(i18n("Opacity: ")); m_options.assistantsGlobalOpacitySlider->setSuffix(" %"); // custom color of selected assistant m_options.customColorOpacitySlider->setValue(100); // 100% m_options.customColorOpacitySlider->setPrefix(i18n("Opacity: ")); m_options.customColorOpacitySlider->setSuffix(" %"); connect(m_options.useCustomAssistantColor, SIGNAL(clicked(bool)), this, SLOT(slotUpdateCustomColor())); connect(m_options.customAssistantColorButton, SIGNAL(changed(QColor)), this, SLOT(slotUpdateCustomColor())); connect(m_options.customColorOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotCustomOpacityChanged())); m_options.vanishingPointAngleSpinbox->setPrefix(i18n("Density: ")); m_options.vanishingPointAngleSpinbox->setSuffix(QChar(Qt::Key_degree)); m_options.vanishingPointAngleSpinbox->setRange(1.0, 180.0); m_options.vanishingPointAngleSpinbox->setSingleStep(0.5); m_options.vanishingPointAngleSpinbox->setVisible(false); } updateToolOptionsUI(); return m_optionsWidget; } void KisAssistantTool::slotGlobalAssistantsColorChanged(const QColor& setColor) { // color and alpha are stored separately, so we need to merge the values before sending it on int oldAlpha = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor().alpha(); QColor newColor = setColor; newColor.setAlpha(oldAlpha); m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor); m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotGlobalAssistantOpacityChanged() { QColor newColor = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor(); qreal newOpacity = m_options.assistantsGlobalOpacitySlider->value() * 0.01 * 255.0; newColor.setAlpha(int(newOpacity)); m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor); m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotUpdateCustomColor() { // get the selected assistant and change the angle value KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { m_selectedAssistant->setUseCustomColor(m_options.useCustomAssistantColor->isChecked()); // changing color doesn't keep alpha, so update that before we send it on QColor newColor = m_options.customAssistantColorButton->color(); newColor.setAlpha(m_selectedAssistant->assistantCustomColor().alpha()); m_selectedAssistant->setAssistantCustomColor(newColor); m_selectedAssistant->uncache(); } updateToolOptionsUI(); m_canvas->canvasWidget()->update(); } void KisAssistantTool::slotCustomOpacityChanged() { KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant(); if (m_selectedAssistant) { QColor newColor = m_selectedAssistant->assistantCustomColor(); qreal newOpacity = m_options.customColorOpacitySlider->value() * 0.01 * 255.0; newColor.setAlpha(int(newOpacity)); m_selectedAssistant->setAssistantCustomColor(newColor); m_selectedAssistant->uncache(); } // this forces the canvas to refresh to see the changes immediately m_canvas->paintingAssistantsDecoration()->uncache(); m_canvas->canvasWidget()->update(); } diff --git a/plugins/assistants/Assistants/kis_assistant_tool.h b/plugins/assistants/Assistants/kis_assistant_tool.h index 0ef7512325..71c4ca524c 100644 --- a/plugins/assistants/Assistants/kis_assistant_tool.h +++ b/plugins/assistants/Assistants/kis_assistant_tool.h @@ -1,176 +1,177 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_ASSISTANT_TOOL_H_ #define _KIS_ASSISTANT_TOOL_H_ #include #include #include #include #include "kis_painting_assistant.h" #include #include #include "ui_AssistantsToolOptions.h" /* The assistant tool allows artists to create special guides on the canvas * to help them with things like perspective and parallel lines * This tool has its own canvas decoration on it that only appears when the tool * is active. This decoration allows people to edit assistant points as well as delete assistants * Many of the operations here are forwarded on to another class (kis_painting_assistant_decoration) * that stores the assistant information as well as the decoration information with lines * * Drawing in two separate classes creates an issue where the editor controls in this class * are covered by the kis_painting_assistant_decoration class. In the future, we probably need to * do all the drawing in one class so we have better control of what is in front */ class KisAssistantTool : public KisTool { Q_OBJECT enum PerspectiveAssistantEditionMode { MODE_CREATION, // This is the mode when there is not yet a perspective grid MODE_EDITING, // This is the mode when the grid has been created, and we are waiting for the user to click on a control box MODE_DRAGGING_NODE, // In this mode one node is translated MODE_DRAGGING_TRANSLATING_TWONODES // This mode is used when creating a new sub perspective grid }; public: KisAssistantTool(KoCanvasBase * canvas); ~KisAssistantTool() override; virtual quint32 priority() { return 3; } /* this is a very big function that has to figure out if we are adding a new assistant, * or editing an existing one when we click on the canvas. There is also a lot of logic * in here that is specific to certain assistants and how they should be handled. * The editor widget is not a UI file, so the move, delete, preview areas have manual * hitbox regions specified to know if a click is doing any of those actions. */ void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; QWidget *createOptionWidget() override; private: // adds and removes assistant. // this is event is forwarded to the kis_painting_decoration class // perspective grids seem to be managed in two places with these calls void addAssistant(); void removeAssistant(KisPaintingAssistantSP assistant); void assistantSelected(KisPaintingAssistantSP assistant); public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; void slotChangeVanishingPointAngle(double value); private Q_SLOTS: void removeAllAssistants(); void saveAssistants(); void loadAssistants(); void updateToolOptionsUI(); /// send the color and opacity information from the UI to the kis_painting_decoration /// which manages the assistants void slotGlobalAssistantsColorChanged(const QColor&); void slotGlobalAssistantOpacityChanged(); void slotUpdateCustomColor(); void slotCustomOpacityChanged(); protected: /// Draws the editor widget controls with move, activate, and delete /// This also creates a lot of assistant specific stuff for vanishing points and perspective grids /// Whatever is painted here will be underneath the content painted in the kis_painting_assistant_decoration /// The kis_painting_assistant_decoration paints the final assistant, so this is more of just editor controls void paint(QPainter& gc, const KoViewConverter &converter) override; protected: /// this class manipulates the kis_painting_assistant_decorations a lot, so this class is a helper /// to get a reference to it and call "updateCanvas" which refreshes the display QPointer m_canvas; /// the handles are retrieved from the kis_painting_decoration originally /// They are used here to generate and manipulate editor handles with the tool's primary action QList m_handles; QList m_sideHandles; KisPaintingAssistantHandleSP m_handleDrag; KisPaintingAssistantHandleSP m_handleCombine; KisPaintingAssistantSP m_assistantDrag; /// Used while a new assistant is being created. Most assistants need multiple points to exist /// so this helps manage the visual state while this creation process is going on KisPaintingAssistantSP m_newAssistant; QPointF m_cursorStart; QPointF m_currentAdjustment; Ui::AssistantsToolOptions m_options; QWidget* m_optionsWidget; QPointF m_dragStart; QLineF m_radius; bool m_snapIsRadial; QPointF m_dragEnd; int m_handleSize; // how large the editor handles will appear private: void drawEditorWidget(KisPaintingAssistantSP assistant, QPainter& _gc); PerspectiveAssistantEditionMode m_internalMode; KisPaintingAssistantHandleSP m_selectedNode1, m_selectedNode2, m_higlightedNode; int m_assistantHelperYOffset; // used by the assistant editor icons for placement on the canvas. QList m_origAssistantList; }; class KisAssistantToolFactory : public KoToolFactoryBase { public: KisAssistantToolFactory() : KoToolFactoryBase("KisAssistantTool") { setToolTip(i18n("Assistant Tool")); setSection(TOOL_TYPE_VIEW); setIconName(koIconNameCStr("krita_tool_assistant")); setPriority(0); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisAssistantToolFactory() override {} KoToolBase * createTool(KoCanvasBase * canvas) override { return new KisAssistantTool(canvas); } }; #endif diff --git a/plugins/dockers/advancedcolorselector/colorselectorng.cpp b/plugins/dockers/advancedcolorselector/colorselectorng.cpp index 3cec60c825..b4ce2ade0f 100644 --- a/plugins/dockers/advancedcolorselector/colorselectorng.cpp +++ b/plugins/dockers/advancedcolorselector/colorselectorng.cpp @@ -1,86 +1,87 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "colorselectorng.h" #include #include #include #include "kis_color_selector_ng_dock.h" #include "kis_color_selector_settings.h" #include "kis_preference_set_registry.h" K_PLUGIN_FACTORY_WITH_JSON(ColorSelectorNgPluginFactory, "krita_colorselectorng.json", registerPlugin();) class ColorSelectorNgDockFactory : public KoDockFactoryBase { public: ColorSelectorNgDockFactory() { } QString id() const override { return QString("ColorSelectorNg"); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { KisColorSelectorNgDock * dockWidget = new KisColorSelectorNgDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; ColorSelectorNgPlugin::ColorSelectorNgPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new ColorSelectorNgDockFactory()); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); KisColorSelectorSettingsFactory* settingsFactory = new KisColorSelectorSettingsFactory(); //load and save preferences //if something in kritarc is missing, then the default from this load function will be used and saved back to kconfig. //this way, cfg.readEntry() in any part won't be able to set its own default KisPreferenceSet* settings = settingsFactory->createPreferenceSet(); Q_ASSERT(settings); settings->loadPreferences(); settings->savePreferences(); delete settings; preferenceSetRegistry->add("KisColorSelectorSettingsFactory", settingsFactory); } ColorSelectorNgPlugin::~ColorSelectorNgPlugin() { } #include "colorselectorng.moc" diff --git a/plugins/dockers/advancedcolorselector/colorselectorng.h b/plugins/dockers/advancedcolorselector/colorselectorng.h index 08da99cf15..32a93bbb8b 100644 --- a/plugins/dockers/advancedcolorselector/colorselectorng.h +++ b/plugins/dockers/advancedcolorselector/colorselectorng.h @@ -1,37 +1,38 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _COLORSELECTORNG_H_ #define _COLORSELECTORNG_H_ #include #include /** * Template of view plugin */ class ColorSelectorNgPlugin : public QObject { Q_OBJECT public: ColorSelectorNgPlugin(QObject *parent, const QVariantList &); ~ColorSelectorNgPlugin() override; }; #endif diff --git a/plugins/dockers/advancedcolorselector/kis_color_history.cpp b/plugins/dockers/advancedcolorselector/kis_color_history.cpp index f4312b67a6..0dfdc0a3b0 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_history.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_history.cpp @@ -1,80 +1,81 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_history.h" #include "kis_canvas2.h" #include "KisViewManager.h" #include "KisView.h" #include "kis_canvas_resource_provider.h" #include #include KisColorHistory::KisColorHistory(QWidget *parent) : KisColorPatches("lastUsedColors", parent) , m_resourceProvider(0) { } void KisColorHistory::unsetCanvas() { KisColorPatches::unsetCanvas(); m_resourceProvider = 0; } void KisColorHistory::setCanvas(KisCanvas2 *canvas) { if (!canvas) return; KisColorPatches::setCanvas(canvas); if (m_resourceProvider) { m_resourceProvider->disconnect(this); } m_resourceProvider = canvas->imageView()->resourceProvider(); connect(canvas->imageView()->resourceProvider(), SIGNAL(sigFGColorUsed(KoColor)), this, SLOT(addColorToHistory(KoColor)), Qt::UniqueConnection); } KisColorSelectorBase* KisColorHistory::createPopup() const { KisColorHistory* ret = new KisColorHistory(); ret->setCanvas(m_canvas); ret->setColors(colors()); ret->m_colorHistory=m_colorHistory; return ret; } void KisColorHistory::addColorToHistory(const KoColor& color) { // don't add color in erase mode. See https://bugs.kde.org/show_bug.cgi?id=298940 if (m_resourceProvider && m_resourceProvider->currentCompositeOp() == COMPOSITE_ERASE) return; m_colorHistory.removeAll(color); m_colorHistory.prepend(color); //the history holds 200 colors, but not all are displayed if (m_colorHistory.size()>200) { m_colorHistory.removeLast(); } setColors(m_colorHistory); } diff --git a/plugins/dockers/advancedcolorselector/kis_color_history.h b/plugins/dockers/advancedcolorselector/kis_color_history.h index f6055fe0ab..630e3cf2f8 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_history.h +++ b/plugins/dockers/advancedcolorselector/kis_color_history.h @@ -1,45 +1,46 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_COLOR_HISTORY_H #define KIS_COLOR_HISTORY_H #include "kis_color_patches.h" class KisCanvasResourceProvider; class KisColorHistory : public KisColorPatches { Q_OBJECT public: explicit KisColorHistory(QWidget *parent = 0); void setCanvas(KisCanvas2 *canvas) override; void unsetCanvas() override; protected: KisColorSelectorBase* createPopup() const override; public Q_SLOTS: void addColorToHistory(const KoColor& color); private: QList m_colorHistory; KisCanvasResourceProvider *m_resourceProvider; // to disconnect... }; #endif // KIS_COLOR_HISTORY_H diff --git a/plugins/dockers/advancedcolorselector/kis_color_patches.cpp b/plugins/dockers/advancedcolorselector/kis_color_patches.cpp index f54557494c..260a70872d 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_patches.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_patches.cpp @@ -1,352 +1,353 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_patches.h" #include #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "KoCanvasResourceProvider.h" #include "kis_display_color_converter.h" KisColorPatches::KisColorPatches(QString configPrefix, QWidget *parent) : KisColorSelectorBase(parent), m_allowColorListChangeGuard(true), m_scrollValue(0), m_configPrefix(configPrefix) { resize(1, 1); updateSettings(); } void KisColorPatches::setColors(QListcolors) { if (m_allowColorListChangeGuard) { m_colors = colors; m_allowColorListChangeGuard=false; KisColorPatches* parent = dynamic_cast(m_parent); if (parent) parent->setColors(colors); KisColorPatches* popup = dynamic_cast(m_popup); if (popup) popup->setColors(colors); m_allowColorListChangeGuard=true; update(); } } void KisColorPatches::paintEvent(QPaintEvent* e) { QPainter painter(this); if(m_allowScrolling) { if(m_direction == Vertical) painter.translate(0, m_scrollValue); else painter.translate(m_scrollValue, 0); } int widgetWidth = width(); int numPatchesInARow = qMax(widgetWidth/m_patchWidth, 1); int widgetHeight = height(); int numPatchesInACol = qMax(widgetHeight/m_patchHeight, 1); for(int i = m_buttonList.size(); i < qMin(fieldCount(), m_colors.size() + m_buttonList.size()); i++) { int row; int col; if(m_direction == Vertical) { row = i /numPatchesInARow; col = i % numPatchesInARow; } else { row= i % numPatchesInACol; col = i / numPatchesInACol; } QColor qcolor = converter()->toQColor(m_colors.at(i - m_buttonList.size())); painter.fillRect(col*m_patchWidth, row*m_patchHeight, m_patchWidth, m_patchHeight, qcolor); } QWidget::paintEvent(e); } void KisColorPatches::wheelEvent(QWheelEvent* event) { m_scrollValue+=event->delta()/2; if(m_direction == Vertical) { if(m_scrollValue < -1*(heightOfAllPatches()-height())) m_scrollValue = -1*(heightOfAllPatches()-height()); } else { if(m_scrollValue < -1*(widthOfAllPatches()-width())) m_scrollValue = -1*(widthOfAllPatches()-width()); } if(m_scrollValue>0) m_scrollValue=0; update(); } void KisColorPatches::resizeEvent(QResizeEvent* event) { if(size()==QSize(1, 1)) return; QWheelEvent dummyWheelEvent(QPoint(), 0, Qt::NoButton, Qt::NoModifier); wheelEvent(&dummyWheelEvent); if(parentWidget()==0) { // this instance is a popup setMinimumWidth(m_patchWidth*(m_patchCount/4)); setMaximumWidth(minimumWidth()); } if (m_allowScrolling == false && event->oldSize() != event->size()) { if(m_direction == Horizontal) { setMaximumHeight(heightForWidth(width())); setMinimumHeight(heightForWidth(width())); } else { setMaximumWidth(widthForHeight(height())); setMinimumWidth(widthForHeight(height())); } } } void KisColorPatches::mouseReleaseEvent(QMouseEvent* event) { KisColorSelectorBase::mouseReleaseEvent(event); event->setAccepted(false); KisColorSelectorBase::mouseReleaseEvent(event); if (event->isAccepted() || !rect().contains(event->pos())) return; if (!m_canvas) return; KoColor color; if(colorAt(event->pos(), &color)) { if (event->button()==Qt::LeftButton) m_canvas->resourceManager()->setForegroundColor(color); else if (event->button()==Qt::RightButton) m_canvas->resourceManager()->setBackgroundColor(color); } } void KisColorPatches::mousePressEvent(QMouseEvent *event) { KoColor koColor; if(!colorAt(event->pos(), &koColor)) return; KisColorSelectorBase::mousePressEvent(event); if(event->isAccepted()) return; updateColorPreview(koColor); if (event->button() == Qt::LeftButton) m_dragStartPos = event->pos(); } void KisColorPatches::mouseMoveEvent(QMouseEvent *event) { event->ignore(); KisColorSelectorBase::mouseMoveEvent(event); if(event->isAccepted()) return; if (!(event->buttons() & Qt::LeftButton)) return; if ((event->pos() - m_dragStartPos).manhattanLength() < QApplication::startDragDistance()) return; KoColor koColor; if(!colorAt(m_dragStartPos, &koColor)) return; QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; QColor color = converter()->toQColor(koColor); mimeData->setColorData(color); mimeData->setText(color.name()); drag->setMimeData(mimeData); drag->exec(Qt::CopyAction); event->accept(); } int KisColorPatches::patchCount() const { return m_patchCount; } bool KisColorPatches::colorAt(const QPoint &pos, KoColor *result) const { if(!rect().contains(pos)) return false; int scrollX = m_direction==Horizontal?m_scrollValue:0; int scrollY = m_direction==Vertical?m_scrollValue:0; int column = (pos.x()-scrollX)/m_patchWidth; int row = (pos.y()-scrollY)/m_patchHeight; int patchNr; if(m_direction == Vertical) { int patchesInARow = width()/m_patchWidth; patchNr=row*patchesInARow+column; } else { // Vertical int patchesInAColumn = height()/m_patchHeight; patchNr=column*patchesInAColumn+row; } patchNr-=m_buttonList.size(); if(patchNr>=0 && patchNr buttonList) { for(int i=0; isetParent(this); } m_buttonList = buttonList; } void KisColorPatches::updateSettings() { KisColorSelectorBase::updateSettings(); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if(cfg.readEntry(m_configPrefix+"Alignment", false)) m_direction=Vertical; else m_direction=Horizontal; m_allowScrolling=cfg.readEntry(m_configPrefix+"Scrolling", true); m_numCols=cfg.readEntry(m_configPrefix+"NumCols", 1); m_numRows=cfg.readEntry(m_configPrefix+"NumRows", 1); m_patchCount=cfg.readEntry(m_configPrefix+"Count", 15); m_patchWidth=cfg.readEntry(m_configPrefix+"Width", 20); m_patchHeight=cfg.readEntry(m_configPrefix+"Height", 20); if(m_patchHeight == 0) { m_patchHeight = 1; } if(parentWidget()==0) { // this instance is a popup m_allowScrolling = false; m_direction = Horizontal; m_patchWidth*=2; m_patchHeight*=2; } for(int i=0; isetGeometry(0, i*m_patchHeight, m_patchWidth, m_patchHeight); } setMaximumWidth(QWIDGETSIZE_MAX); setMinimumWidth(1); setMaximumHeight(QWIDGETSIZE_MAX); setMinimumHeight(1); if(m_allowScrolling && m_direction == Horizontal) { setMaximumHeight(m_numRows*m_patchHeight); setMinimumHeight(m_numRows*m_patchHeight); } if(m_allowScrolling && m_direction == Vertical) { setMaximumWidth(m_numCols*m_patchWidth); setMinimumWidth(m_numCols*m_patchWidth); } if(m_allowScrolling == false) { m_scrollValue = 0; } QResizeEvent dummy(size(), QSize(-1,-1)); resizeEvent(&dummy); setPopupBehaviour(false, false); update(); } int KisColorPatches::widthOfAllPatches() { return (fieldCount()/m_numRows)*m_patchWidth; } int KisColorPatches::heightOfAllPatches() { return (fieldCount()/m_numCols)*m_patchHeight; } int KisColorPatches::heightForWidth(int width) const { int numPatchesInARow = width / m_patchWidth; int numRows = qMax((fieldCount() - 1), 1) / qMax(numPatchesInARow + 1, 1); return qMax(numRows * m_patchHeight, m_patchHeight); } int KisColorPatches::widthForHeight(int height) const { if (height == 0) { return 0; } if (m_patchHeight == 0) { return 0; } int numPatchesInACol = height / m_patchHeight; int numCols = (fieldCount() - 1) / (numPatchesInACol + 1); return qMax(numCols*m_patchWidth, m_patchWidth); } int KisColorPatches::fieldCount() const { return m_patchCount+m_buttonList.size(); } diff --git a/plugins/dockers/advancedcolorselector/kis_color_patches.h b/plugins/dockers/advancedcolorselector/kis_color_patches.h index 1905d4c102..e9bc5c1c0e 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_patches.h +++ b/plugins/dockers/advancedcolorselector/kis_color_patches.h @@ -1,89 +1,90 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_COLOR_PATCHES_H #define KIS_COLOR_PATCHES_H #include "kis_color_selector_base.h" #include "KoColor.h" class KoColor; class KisColorPatches : public KisColorSelectorBase { Q_OBJECT public: explicit KisColorPatches(QString configPrefix, QWidget *parent = 0); enum Direction { Horizontal, Vertical }; public Q_SLOTS: void updateSettings() override; protected: void setColors(QList colors); QList colors() const {return m_colors;} void paintEvent(QPaintEvent *) override; void wheelEvent(QWheelEvent *) override; void resizeEvent(QResizeEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; void mousePressEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; int patchCount() const; bool colorAt(const QPoint &, KoColor *result) const; public: /// set buttons, that should be drawn additionally to the patches /// this class takes ownership of them and will delete them /// they will be resized to the patchsize void setAdditionalButtons(QList buttonList); private: int m_patchWidth; int m_patchHeight; int m_patchCount; QList m_colors; bool m_allowColorListChangeGuard; int m_scrollValue; Direction m_direction; bool m_allowScrolling; int m_numCols; int m_numRows; QList m_buttonList; /// returns width of the patchfield, if there are only m_numRows allowed int widthOfAllPatches(); /// returns height of the patchfield, if there are only m_numCols allowed int heightOfAllPatches(); /// returns height, that is needed to display all patches with the given width int heightForWidth(int width) const override; /// returns width, that is needed to display all patches with the given height int widthForHeight(int height) const; /// returns count of colors and buttons int fieldCount() const; QString m_configPrefix; QPoint m_dragStartPos; }; #endif diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector.cpp index 03e2a725ca..6054d7d714 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_selector.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector.cpp @@ -1,395 +1,396 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_selector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_color_selector_ring.h" #include "kis_color_selector_triangle.h" #include "kis_color_selector_simple.h" #include "kis_color_selector_wheel.h" #include "kis_color_selector_container.h" #include "kis_canvas2.h" #include "kis_signal_compressor.h" #include "KisViewManager.h" KisColorSelector::KisColorSelector(KisColorSelectorConfiguration conf, QWidget* parent) : KisColorSelectorBase(parent), m_ring(0), m_triangle(0), m_slider(0), m_square(0), m_wheel(0), m_mainComponent(0), m_subComponent(0), m_grabbingComponent(0), m_blipDisplay(true) { init(); updateSettings(); setConfiguration(conf); } KisColorSelector::KisColorSelector(QWidget* parent) : KisColorSelectorBase(parent), m_ring(0), m_triangle(0), m_slider(0), m_square(0), m_wheel(0), m_button(0), m_mainComponent(0), m_subComponent(0), m_grabbingComponent(0), m_blipDisplay(true) { init(); updateSettings(); } KisColorSelectorBase* KisColorSelector::createPopup() const { KisColorSelectorBase* popup = new KisColorSelector(0); popup->setColor(m_lastRealColor); return popup; } void KisColorSelector::setConfiguration(KisColorSelectorConfiguration conf) { m_configuration = conf; if(m_mainComponent!=0) { Q_ASSERT(m_subComponent!=0); m_mainComponent->setGeometry(0, 0, 0, 0); m_subComponent->setGeometry(0, 0, 0, 0); m_mainComponent->disconnect(); m_subComponent->disconnect(); } switch (m_configuration.mainType) { case KisColorSelectorConfiguration::Square: m_mainComponent=m_square; break; case KisColorSelectorConfiguration::Wheel: m_mainComponent=m_wheel; break; case KisColorSelectorConfiguration::Triangle: m_mainComponent=m_triangle; break; default: Q_ASSERT(false); } switch (m_configuration.subType) { case KisColorSelectorConfiguration::Ring: m_subComponent=m_ring; break; case KisColorSelectorConfiguration::Slider: m_subComponent=m_slider; break; default: Q_ASSERT(false); } connect(m_mainComponent, SIGNAL(paramChanged(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)), m_subComponent, SLOT(setParam(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)), Qt::UniqueConnection); connect(m_subComponent, SIGNAL(paramChanged(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)), m_mainComponent, SLOT(setParam(qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal,qreal)), Qt::UniqueConnection); connect(m_mainComponent, SIGNAL(update()), m_signalCompressor, SLOT(start()), Qt::UniqueConnection); connect(m_subComponent, SIGNAL(update()), m_signalCompressor, SLOT(start()), Qt::UniqueConnection); m_mainComponent->setConfiguration(m_configuration.mainTypeParameter, m_configuration.mainType); m_subComponent->setConfiguration(m_configuration.subTypeParameter, m_configuration.subType); QResizeEvent event(QSize(width(), height()), QSize()); resizeEvent(&event); } KisColorSelectorConfiguration KisColorSelector::configuration() const { return m_configuration; } void KisColorSelector::updateSettings() { KisColorSelectorBase::updateSettings(); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); setConfiguration(KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelectorConfiguration().toString()))); } void KisColorSelector::slotGamutMaskSet(KoGamutMaskSP gamutMask) { m_mainComponent->setGamutMask(gamutMask); m_subComponent->setGamutMask(gamutMask); slotGamutMaskToggle(true); } void KisColorSelector::slotGamutMaskUnset() { m_mainComponent->unsetGamutMask(); m_subComponent->unsetGamutMask(); slotGamutMaskToggle(false); } void KisColorSelector::slotGamutMaskPreviewUpdate() { m_mainComponent->updateGamutMaskPreview(); m_subComponent->updateGamutMaskPreview(); } void KisColorSelector::slotGamutMaskToggle(bool state) { m_mainComponent->toggleGamutMask(state); m_subComponent->toggleGamutMask(state); } void KisColorSelector::updateIcons() { if (m_button) { m_button->setIcon(KisIconUtils::loadIcon("configure")); } } void KisColorSelector::hasAtLeastOneDocument(bool value) { m_hasAtLeastOneDocumentOpen = value; } void KisColorSelector::reset() { if (m_mainComponent) { m_mainComponent->setDirty(); } if (m_subComponent) { m_subComponent->setDirty(); } KisColorSelectorBase::reset(); } void KisColorSelector::paintEvent(QPaintEvent* e) { Q_UNUSED(e); QPainter p(this); p.fillRect(0,0,width(), height(), QColor(128,128,128)); p.setRenderHint(QPainter::Antialiasing); // this variable name isn't entirely accurate to what always happens. see definition in header file to understand it better if (!m_hasAtLeastOneDocumentOpen) { p.setOpacity(0.2); } m_mainComponent->paintEvent(&p); m_subComponent->paintEvent(&p); p.setOpacity(1.0); } inline int iconSize(qreal width, qreal height) { qreal radius = qMin(width, height)/2.; qreal xm = width/2.; qreal ym = height/2.; if(xm>=2*ym || ym>=2*xm) return qBound(5., radius, 32.); qreal a=-2; qreal b=2.*(xm+ym); qreal c=radius*radius-xm*xm-ym*ym; return qBound(5., ((-b+sqrt(b*b-4*a*c))/(2*a)), 32.); } void KisColorSelector::resizeEvent(QResizeEvent* e) { if (m_configuration.subType == KisColorSelectorConfiguration::Ring) { m_ring->setGeometry(0,0,width(), height()); if (displaySettingsButton()) { int size = iconSize(width(), height()); m_button->setGeometry(0, 0, size, size); } if (m_configuration.mainType == KisColorSelectorConfiguration::Triangle) { m_triangle->setGeometry(width()/2-m_ring->innerRadius(), height()/2-m_ring->innerRadius(), m_ring->innerRadius()*2, m_ring->innerRadius()*2); } else { int size = m_ring->innerRadius()*2/sqrt(2.); m_square->setGeometry(width()/2-size/2, height()/2-size/2, size, size); } } else { // type wheel and square if (m_configuration.mainType == KisColorSelectorConfiguration::Wheel) { if(displaySettingsButton()) { int size = iconSize(width(), height()*0.9); m_button->setGeometry(0, height()*0.1, size, size); } m_mainComponent->setGeometry(0, height()*0.1, width(), height()*0.9); m_subComponent->setGeometry( 0, 0, width(), height()*0.1); } else { int buttonSize = 0; if(displaySettingsButton()) { buttonSize = qBound(20, int(0.1*height()), 32); m_button->setGeometry(0, 0, buttonSize, buttonSize); } if(height()>width()) { int selectorHeight=height()-buttonSize; m_mainComponent->setGeometry(0, buttonSize+selectorHeight*0.1, width(), selectorHeight*0.9); m_subComponent->setGeometry( 0, buttonSize, width(), selectorHeight*0.1); } else { int selectorWidth=width()-buttonSize; m_mainComponent->setGeometry(buttonSize, height()*0.1, selectorWidth, height()*0.9); m_subComponent->setGeometry( buttonSize, 0, selectorWidth, height()*0.1); } } } // reset the correct color after resizing the widget setColor(m_lastRealColor); KisColorSelectorBase::resizeEvent(e); } void KisColorSelector::mousePressEvent(QMouseEvent* e) { e->setAccepted(false); KisColorSelectorBase::mousePressEvent(e); if(!e->isAccepted()) { if(m_mainComponent->wantsGrab(e->x(), e->y())) m_grabbingComponent=m_mainComponent; else if(m_subComponent->wantsGrab(e->x(), e->y())) m_grabbingComponent=m_subComponent; mouseEvent(e); updatePreviousColorPreview(); e->accept(); } } void KisColorSelector::mouseMoveEvent(QMouseEvent* e) { KisColorSelectorBase::mouseMoveEvent(e); mouseEvent(e); e->accept(); } void KisColorSelector::mouseReleaseEvent(QMouseEvent* e) { e->setAccepted(false); KisColorSelectorBase::mouseReleaseEvent(e); if(!e->isAccepted() && !(m_lastRealColor == m_currentRealColor)) { m_lastRealColor = m_currentRealColor; m_lastColorRole = Acs::buttonToRole(e->button()); updateColor(m_lastRealColor, m_lastColorRole, false); updateBaseColorPreview(m_currentRealColor); e->accept(); } m_grabbingComponent=0; } bool KisColorSelector::displaySettingsButton() { return dynamic_cast(parent()); } void KisColorSelector::setColor(const KoColor &color) { m_mainComponent->setColor(color); m_subComponent->setColor(color); m_lastRealColor = color; m_signalCompressor->start(); } void KisColorSelector::mouseEvent(QMouseEvent *e) { if (m_grabbingComponent && (e->buttons() & Qt::LeftButton || e->buttons() & Qt::RightButton)) { m_grabbingComponent->mouseEvent(e->x(), e->y()); KoColor color = m_mainComponent->currentColor(); Acs::ColorRole role = Acs::buttonsToRole(e->button(), e->buttons()); m_currentRealColor = color; requestUpdateColorAndPreview(color, role); } } void KisColorSelector::init() { setAcceptDrops(true); m_lastColorRole = Acs::Foreground; m_ring = new KisColorSelectorRing(this); m_triangle = new KisColorSelectorTriangle(this); m_slider = new KisColorSelectorSimple(this); m_square = new KisColorSelectorSimple(this); m_wheel = new KisColorSelectorWheel(this); if(displaySettingsButton()) { m_button = new QPushButton(this); m_button->setIcon(KisIconUtils::loadIcon("configure")); m_button->setFlat(true); connect(m_button, SIGNAL(clicked()), SIGNAL(settingsButtonClicked())); } // a tablet can send many more signals, than a mouse // this causes many repaints, if updating after every signal. m_signalCompressor = new KisSignalCompressor(20, KisSignalCompressor::FIRST_INACTIVE, this); connect(m_signalCompressor, SIGNAL(timeout()), SLOT(update())); setMinimumSize(40, 40); } diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector.h b/plugins/dockers/advancedcolorselector/kis_color_selector.h index b9053e6b22..564ac57c70 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_selector.h +++ b/plugins/dockers/advancedcolorselector/kis_color_selector.h @@ -1,105 +1,106 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_COLOR_SELECTOR_H #define KIS_COLOR_SELECTOR_H #include "kis_color_selector_base.h" #include #include class KisColorSelectorRing; class KisColorSelectorComponent; class KisColorSelectorSimple; class KisColorSelectorWheel; class QPushButton; class KisSignalCompressor; class KisColorSelector : public KisColorSelectorBase { Q_OBJECT public: KisColorSelector(KisColorSelectorConfiguration conf, QWidget* parent = 0); KisColorSelector(QWidget* parent=0); KisColorSelectorBase* createPopup() const override; void setConfiguration(KisColorSelectorConfiguration conf); KisColorSelectorConfiguration configuration() const; void setColor(const KoColor &color) override; /// update icons when a theme update happens void updateIcons(); void hasAtLeastOneDocument(bool value); public Q_SLOTS: void reset() override; void updateSettings() override; void slotGamutMaskSet(KoGamutMaskSP gamutMask); void slotGamutMaskUnset(); void slotGamutMaskPreviewUpdate(); void slotGamutMaskToggle(bool state); Q_SIGNALS: void settingsButtonClicked(); protected: void paintEvent(QPaintEvent*) override; void resizeEvent(QResizeEvent*) override; void mousePressEvent(QMouseEvent*) override; void mouseMoveEvent(QMouseEvent*) override; void mouseReleaseEvent(QMouseEvent*) override; bool displaySettingsButton(); private: void mouseEvent(QMouseEvent* e); void init(); KisColorSelectorRing* m_ring; KisColorSelectorComponent* m_triangle; KisColorSelectorSimple* m_slider; KisColorSelectorSimple* m_square; KisColorSelectorWheel* m_wheel; QPushButton* m_button; KisColorSelectorComponent* m_mainComponent; KisColorSelectorComponent* m_subComponent; KisColorSelectorComponent* m_grabbingComponent; KisSignalCompressor *m_signalCompressor; KisColorSelectorConfiguration m_configuration; KoColor m_lastRealColor; KoColor m_currentRealColor; bool m_blipDisplay; Acs::ColorRole m_lastColorRole; /// if Krita starts with a reference to this component that is attached to a canvas, it will call setCanvas() /// that check will be what ultimately decides whether this component will look enabled or disabled /// This color selector is sometimes not attached to the canvas, so we shouldn't disable it in that situation /// One instance of that is when you select the color wheel type from the settings. bool m_hasAtLeastOneDocumentOpen = true; public: void setDisplayBlip(bool disp) {m_blipDisplay = disp;} bool displayBlip() const {return m_blipDisplay;} }; #endif // KIS_COLSELNG_COLOR_SELECTOR_H diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.cpp index 93968eea1b..2080940e35 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.cpp @@ -1,49 +1,50 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_selector_ng_dock.h" #include #include "kis_canvas2.h" #include "kis_color_selector_ng_docker_widget.h" KisColorSelectorNgDock::KisColorSelectorNgDock() : QDockWidget() { m_colorSelectorNgWidget = new KisColorSelectorNgDockerWidget(this); setWidget(m_colorSelectorNgWidget); m_colorSelectorNgWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setWindowTitle(i18n("Advanced Color Selector")); } void KisColorSelectorNgDock::setCanvas(KoCanvasBase * canvas) { setEnabled(canvas != 0); m_colorSelectorNgWidget->setCanvas(dynamic_cast(canvas)); } void KisColorSelectorNgDock::unsetCanvas() { setEnabled(false); m_colorSelectorNgWidget->unsetCanvas(); } diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.h b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.h index 7df03d9a10..21e8f69d82 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.h +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_dock.h @@ -1,41 +1,42 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_COLOR_SELECTOR_NG_DOCKER_H #define KIS_COLOR_SELECTOR_NG_DOCKER_H #include #include class KisColorSelectorNgDockerWidget; class KisColorSelectorNgDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: KisColorSelectorNgDock(); QString observerName() override { return "KisColorSelectorNgDock"; } /// reimplemented from KoCanvasObserverBase void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; private: KisColorSelectorNgDockerWidget *m_colorSelectorNgWidget; }; #endif diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp index 511701c76a..494747a628 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.cpp @@ -1,285 +1,286 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_selector_ng_docker_widget.h" #include "ui_wdg_color_selector_settings.h" #include #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "KisViewManager.h" #include "kis_node_manager.h" #include "kis_canvas_resource_provider.h" #include "kis_color_space_selector.h" #include "kis_preference_set_registry.h" #include "kis_node.h" #include "kis_paint_device.h" #include "kis_color_history.h" #include "kis_common_colors.h" #include "kis_color_selector_settings.h" #include "kis_color_selector_container.h" #include "kis_action_registry.h" KisColorSelectorNgDockerWidget::KisColorSelectorNgDockerWidget(QWidget *parent) : QWidget(parent), m_colorHistoryAction(0), m_commonColorsAction(0), m_widgetLayout(0), m_mainLayout(0), m_horizontalPatchesContainer(0), m_sidebarLayout(0), m_verticalColorPatchesLayout(0), m_horizontalColorPatchesLayout(0), m_fallbackSettingsButton(new QToolButton(this)), m_canvas(0) { setAutoFillBackground(true); m_colorSelectorContainer = new KisColorSelectorContainer(this); m_colorHistoryWidget = new KisColorHistory(this); m_commonColorsWidget = new KisCommonColors(this); //default settings //remember to also change the default in the ui file //shade selector // fallback settings button when the color selector is disabled m_fallbackSettingsButton->setIcon(KisIconUtils::loadIcon("configure")); m_fallbackSettingsButton->setIconSize(QSize(22,22)); m_fallbackSettingsButton->setAutoRaise(true); m_fallbackSettingsButton->hide(); //layout m_widgetLayout = new QHBoxLayout(); m_widgetLayout->setSpacing(0); m_widgetLayout->setMargin(0); m_mainLayout = new QVBoxLayout(); m_mainLayout->setSpacing(0); m_mainLayout->setMargin(0); m_horizontalPatchesContainer = new QHBoxLayout(); m_horizontalPatchesContainer->setSpacing(0); m_horizontalPatchesContainer->setMargin(0); m_sidebarLayout = new QVBoxLayout(); m_sidebarLayout->setSpacing(0); m_sidebarLayout->setMargin(0); m_verticalColorPatchesLayout = new QHBoxLayout(); m_verticalColorPatchesLayout->setSpacing(0); m_verticalColorPatchesLayout->setMargin(0); m_horizontalColorPatchesLayout = new QVBoxLayout(); m_horizontalColorPatchesLayout->setSpacing(0); m_horizontalColorPatchesLayout->setMargin(0); m_horizontalPatchesContainer->addLayout(m_horizontalColorPatchesLayout); m_mainLayout->addWidget(m_colorSelectorContainer); m_mainLayout->addLayout(m_horizontalPatchesContainer); m_sidebarLayout->addLayout(m_verticalColorPatchesLayout); m_widgetLayout->addLayout(m_mainLayout); m_widgetLayout->addLayout(m_sidebarLayout); setLayout(m_widgetLayout); updateLayout(); connect(m_colorSelectorContainer, SIGNAL(openSettings()), this, SLOT(openSettings())); //emit settingsChanged() if the settings are changed in krita preferences KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); KisColorSelectorSettingsFactory* factory = dynamic_cast(preferenceSetRegistry->get("KisColorSelectorSettingsFactory")); Q_ASSERT(factory); connect(&(factory->repeater), SIGNAL(settingsUpdated()), this, SIGNAL(settingsChanged()), Qt::UniqueConnection); connect(this, SIGNAL(settingsChanged()), this, SLOT(updateLayout()), Qt::UniqueConnection); connect(this, SIGNAL(settingsChanged()), m_commonColorsWidget, SLOT(updateSettings()), Qt::UniqueConnection); connect(this, SIGNAL(settingsChanged()), m_colorHistoryWidget, SLOT(updateSettings()), Qt::UniqueConnection); connect(this, SIGNAL(settingsChanged()), m_colorSelectorContainer, SIGNAL(settingsChanged()), Qt::UniqueConnection); connect(this, SIGNAL(settingsChanged()), this, SLOT(update()), Qt::UniqueConnection); emit settingsChanged(); m_colorHistoryAction = KisActionRegistry::instance()->makeQAction("show_color_history", this); connect(m_colorHistoryAction, SIGNAL(triggered()), m_colorHistoryWidget, SLOT(showPopup()), Qt::UniqueConnection); m_commonColorsAction = KisActionRegistry::instance()->makeQAction("show_common_colors", this); connect(m_commonColorsAction, SIGNAL(triggered()), m_commonColorsWidget, SLOT(showPopup()), Qt::UniqueConnection); connect(m_fallbackSettingsButton, SIGNAL(clicked()), this, SLOT(openSettings())); } void KisColorSelectorNgDockerWidget::unsetCanvas() { m_canvas = 0; m_commonColorsWidget->unsetCanvas(); m_colorHistoryWidget->unsetCanvas(); m_colorSelectorContainer->unsetCanvas(); } void KisColorSelectorNgDockerWidget::setCanvas(KisCanvas2 *canvas) { if (m_canvas) { m_canvas->disconnect(this); KActionCollection *ac = m_canvas->viewManager()->actionCollection(); ac->takeAction(ac->action("show_color_history")); ac->takeAction(ac->action("show_common_colors")); } m_canvas = canvas; m_commonColorsWidget->setCanvas(canvas); m_colorHistoryWidget->setCanvas(canvas); m_colorSelectorContainer->setCanvas(canvas); if (m_canvas && m_canvas->viewManager()) { if (m_canvas->viewManager()->nodeManager()) { connect(m_canvas->viewManager()->nodeManager(), SIGNAL(sigLayerActivated(KisLayerSP)), SLOT(reactOnLayerChange()), Qt::UniqueConnection); } KActionCollection* actionCollection = canvas->viewManager()->actionCollection(); actionCollection->addAction("show_color_history", m_colorHistoryAction); actionCollection->addAction("show_common_colors", m_commonColorsAction); connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), m_colorSelectorContainer, SLOT(slotUpdateIcons()), Qt::UniqueConnection); } reactOnLayerChange(); } void KisColorSelectorNgDockerWidget::openSettings() { if (!m_canvas) return; KisColorSelectorSettingsDialog settings; if(settings.exec()==QDialog::Accepted) { emit settingsChanged(); } } void KisColorSelectorNgDockerWidget::updateLayout() { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); bool showColorSelector = (bool) cfg.readEntry("showColorSelector", true); //color patches bool m_lastColorsShow = cfg.readEntry("lastUsedColorsShow", true); KisColorPatches::Direction m_lastColorsDirection; if(cfg.readEntry("lastUsedColorsAlignment", false)) m_lastColorsDirection=KisColorPatches::Vertical; else m_lastColorsDirection=KisColorPatches::Horizontal; bool m_commonColorsShow = cfg.readEntry("commonColorsShow", true); KisColorPatches::Direction m_commonColorsDirection; if(cfg.readEntry("commonColorsAlignment", false)) m_commonColorsDirection=KisColorPatches::Vertical; else m_commonColorsDirection=KisColorPatches::Horizontal; m_verticalColorPatchesLayout->removeWidget(m_colorHistoryWidget); m_verticalColorPatchesLayout->removeWidget(m_commonColorsWidget); m_horizontalColorPatchesLayout->removeWidget(m_colorHistoryWidget); m_horizontalColorPatchesLayout->removeWidget(m_commonColorsWidget); m_sidebarLayout->removeWidget(m_fallbackSettingsButton); m_mainLayout->removeWidget(m_fallbackSettingsButton); if(m_lastColorsShow==false) m_colorHistoryWidget->hide(); else m_colorHistoryWidget->show(); if(m_commonColorsShow==false) { m_commonColorsWidget->hide(); } else { m_commonColorsWidget->show(); } bool fallbackSettingsButtonVertical = true; if(m_lastColorsShow && m_lastColorsDirection==KisColorPatches::Vertical) { m_verticalColorPatchesLayout->addWidget(m_colorHistoryWidget); } if(m_commonColorsShow && m_commonColorsDirection==KisColorPatches::Vertical) { m_verticalColorPatchesLayout->addWidget(m_commonColorsWidget); } if(m_lastColorsShow && m_lastColorsDirection==KisColorPatches::Horizontal) { m_horizontalColorPatchesLayout->addWidget(m_colorHistoryWidget); fallbackSettingsButtonVertical = false; } if(m_commonColorsShow && m_commonColorsDirection==KisColorPatches::Horizontal) { m_horizontalColorPatchesLayout->addWidget(m_commonColorsWidget); fallbackSettingsButtonVertical = false; } // prefer the vertical column, if patch components have different layout if (m_commonColorsDirection != m_lastColorsDirection) { fallbackSettingsButtonVertical = true; } if (!showColorSelector) { if (fallbackSettingsButtonVertical) { m_sidebarLayout->addWidget(m_fallbackSettingsButton); } else { m_horizontalPatchesContainer->addWidget(m_fallbackSettingsButton); } m_fallbackSettingsButton->show(); } else { m_fallbackSettingsButton->hide(); } updateGeometry(); } void KisColorSelectorNgDockerWidget::reactOnLayerChange() { /** * Trigger the update for the case if some legacy code needs it. * Now the node's color space is managed by the * KisDisplayColorConverter and KisColorSelectorBase objects, so * technically this call is not needed anymore. Please remove it * when you are totally sure this will not break something. */ emit settingsChanged(); } diff --git a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h index 1b39587a4c..e9d51d3c06 100644 --- a/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h +++ b/plugins/dockers/advancedcolorselector/kis_color_selector_ng_docker_widget.h @@ -1,75 +1,76 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_COLOR_SELECTOR_NG_DOCKER_WIDGET_H #define KIS_COLOR_SELECTOR_NG_DOCKER_WIDGET_H #include #include #include #include class QAction; class KisCommonColors; class KisColorHistory; class KisColorSelectorContainer; class QVBoxLayout; class QHBoxLayout; class KisColorSelectorNgDockerWidget : public QWidget { Q_OBJECT public: explicit KisColorSelectorNgDockerWidget(QWidget *parent = 0); void setCanvas(KisCanvas2* canvas); void unsetCanvas(); public Q_SLOTS: void openSettings(); Q_SIGNALS: void settingsChanged(); protected Q_SLOTS: void updateLayout(); void reactOnLayerChange(); private: KisColorSelectorContainer* m_colorSelectorContainer; KisColorHistory* m_colorHistoryWidget; KisCommonColors* m_commonColorsWidget; QAction * m_colorHistoryAction; QAction * m_commonColorsAction; QHBoxLayout* m_widgetLayout; QVBoxLayout* m_mainLayout; QHBoxLayout* m_horizontalPatchesContainer; QVBoxLayout* m_sidebarLayout; QHBoxLayout* m_verticalColorPatchesLayout; // vertical color patches should be added here QVBoxLayout* m_horizontalColorPatchesLayout;//horizontal ----------"---------------------- QToolButton* m_fallbackSettingsButton; QPointer m_canvas; }; #endif diff --git a/plugins/dockers/advancedcolorselector/kis_common_colors.cpp b/plugins/dockers/advancedcolorselector/kis_common_colors.cpp index f8512ea1b3..6664ac6004 100644 --- a/plugins/dockers/advancedcolorselector/kis_common_colors.cpp +++ b/plugins/dockers/advancedcolorselector/kis_common_colors.cpp @@ -1,139 +1,140 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_common_colors.h" #include #include #include #include #include #include #include #include #include #include #include #include "KoColor.h" #include "kis_canvas2.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_config.h" #include "kis_common_colors_recalculation_runner.h" KisCommonColors::KisCommonColors(QWidget *parent) : KisColorPatches("commonColors", parent) { m_reloadButton = new QPushButton(); m_reloadButton->setIcon(KisIconUtils::loadIcon("view-refresh")); m_reloadButton->setToolTip(i18n("Create a list of colors from the image")); connect(m_reloadButton, SIGNAL(clicked()), this, SLOT(recalculate())); QList tmpList; tmpList.append(m_reloadButton); setAdditionalButtons(tmpList); updateSettings(); m_recalculationTimer.setInterval(2000); m_recalculationTimer.setSingleShot(true); connect(&m_recalculationTimer, SIGNAL(timeout()), this, SLOT(recalculate())); } void KisCommonColors::setCanvas(KisCanvas2 *canvas) { KisColorPatches::setCanvas(canvas); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if (cfg.readEntry("commonColorsAutoUpdate", false)) { if (m_image) { m_image->disconnect(this); } if (m_canvas) { connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)), &m_recalculationTimer, SLOT(start()), Qt::UniqueConnection); m_image = m_canvas->image(); } else { m_image = 0; } } } KisColorSelectorBase* KisCommonColors::createPopup() const { KisCommonColors* ret = new KisCommonColors(); ret->setCanvas(m_canvas); ret->setColors(colors()); return ret; } void KisCommonColors::updateSettings() { KisColorPatches::updateSettings(); if(!(m_canvas && m_canvas->image())) return; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if (cfg.readEntry("commonColorsAutoUpdate", false)) { connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)), &m_recalculationTimer, SLOT(start()), Qt::UniqueConnection); } else { disconnect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)), &m_recalculationTimer, SLOT(start())); } m_reloadButton->setEnabled(true); } void KisCommonColors::setColors(QList colors) { QMutexLocker locker(&m_mutex); KisColorPatches::setColors(colors); m_reloadButton->setEnabled(true); m_calculatedColors = colors; } void KisCommonColors::recalculate() { if (!m_canvas) { return; } if(m_reloadButton->isEnabled()==false) { // on old computation is still running // try later to recalculate m_recalculationTimer.start(); return; } m_reloadButton->setEnabled(false); qApp->processEvents(); KisImageWSP kisImage = m_canvas->image(); QImage image = kisImage->projection()->createThumbnail(1024, 1024, kisImage->bounds(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); KisCommonColorsRecalculationRunner* runner = new KisCommonColorsRecalculationRunner(image, patchCount(), this); QThreadPool::globalInstance()->start(runner); } diff --git a/plugins/dockers/advancedcolorselector/kis_common_colors.h b/plugins/dockers/advancedcolorselector/kis_common_colors.h index 47470af25e..77024a2569 100644 --- a/plugins/dockers/advancedcolorselector/kis_common_colors.h +++ b/plugins/dockers/advancedcolorselector/kis_common_colors.h @@ -1,50 +1,51 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_COMMON_COLORS_H #define KIS_COMMON_COLORS_H #include #include #include "kis_color_patches.h" #include class QPushButton; class KisCommonColors : public KisColorPatches { Q_OBJECT public: explicit KisCommonColors(QWidget *parent = 0); void setCanvas(KisCanvas2 *canvas) override; void unsetCanvas() override {} KisColorSelectorBase* createPopup() const override; public Q_SLOTS: void setColors(QList colors); void updateSettings() override; void recalculate(); private: QMutex m_mutex; QTimer m_recalculationTimer; QPushButton* m_reloadButton; QList m_calculatedColors; KisImageWSP m_image; }; #endif diff --git a/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.cpp b/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.cpp index f3fc6b6d30..b33c736e6a 100644 --- a/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.cpp +++ b/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.cpp @@ -1,182 +1,183 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_minimal_shade_selector.h" #include #include #include #include #include #include #include #include "KoCanvasResourceProvider.h" #include "kis_shade_selector_line.h" #include "kis_color_selector_base_proxy.h" KisMinimalShadeSelector::KisMinimalShadeSelector(QWidget *parent) : KisColorSelectorBase(parent) , m_canvas(0) , m_proxy(new KisColorSelectorBaseProxyObject(this)) { setAcceptDrops(true); QVBoxLayout* l = new QVBoxLayout(this); l->setSpacing(0); l->setMargin(0); updateSettings(); setMouseTracking(true); } KisMinimalShadeSelector::~KisMinimalShadeSelector() { } void KisMinimalShadeSelector::unsetCanvas() { KisColorSelectorBase::unsetCanvas(); m_canvas = 0; } void KisMinimalShadeSelector::setCanvas(KisCanvas2 *canvas) { KisColorSelectorBase::setCanvas(canvas); m_canvas = canvas; } void KisMinimalShadeSelector::setColor(const KoColor& color) { m_lastRealColor = color; for(int i=0; isetColor(color); } } void KisMinimalShadeSelector::updateSettings() { KisColorSelectorBase::updateSettings(); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); QString stri = cfg.readEntry("minimalShadeSelectorLineConfig", "0|0.2|0|0"); QStringList strili = stri.split(';', QString::SkipEmptyParts); int lineCount = strili.size(); while(lineCount-m_shadingLines.size() > 0) { KisShadeSelectorLine *line = new KisShadeSelectorLine(m_proxy.data(), this); m_shadingLines.append(line); m_shadingLines.last()->setLineNumber(m_shadingLines.size()-1); layout()->addWidget(m_shadingLines.last()); } while(lineCount-m_shadingLines.size() < 0) { layout()->removeWidget(m_shadingLines.last()); delete m_shadingLines.takeLast(); } for(int i=0; ifromString(strili.at(i)); } int lineHeight = cfg.readEntry("minimalShadeSelectorLineHeight", 20); setMinimumHeight(lineCount*lineHeight+2*lineCount); setMaximumHeight(lineCount*lineHeight+2*lineCount); for(int i=0; iupdateSettings(); setPopupBehaviour(false, false); } void KisMinimalShadeSelector::mousePressEvent(QMouseEvent * e) { Q_FOREACH (KisShadeSelectorLine* line, m_shadingLines) { QMouseEvent newEvent(e->type(), line->mapFromGlobal(e->globalPos()), e->button(), e->buttons(), e->modifiers()); if(line->rect().contains(newEvent.pos())) line->mousePressEvent(&newEvent); } KisColorSelectorBase::mousePressEvent(e); } void KisMinimalShadeSelector::mouseMoveEvent(QMouseEvent * e) { Q_FOREACH (KisShadeSelectorLine* line, m_shadingLines) { QMouseEvent newEvent(e->type(), line->mapFromGlobal(e->globalPos()), e->button(), e->buttons(), e->modifiers()); if(line->rect().contains(newEvent.pos())) line->mouseMoveEvent(&newEvent); } KisColorSelectorBase::mouseMoveEvent(e); } void KisMinimalShadeSelector::mouseReleaseEvent(QMouseEvent * e) { Q_FOREACH (KisShadeSelectorLine* line, m_shadingLines) { QMouseEvent newEvent(e->type(), line->mapFromGlobal(e->globalPos()), e->button(), e->buttons(), e->modifiers()); if(line->rect().contains(newEvent.pos())) line->mouseReleaseEvent(&newEvent); } KisColorSelectorBase::mouseReleaseEvent(e); } void KisMinimalShadeSelector::canvasResourceChanged(int key, const QVariant &v) { if(m_colorUpdateAllowed==false) return; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); bool onForeground = cfg.readEntry("shadeSelectorUpdateOnForeground", false); bool onBackground = cfg.readEntry("shadeSelectorUpdateOnBackground", true); if ((key == KoCanvasResourceProvider::ForegroundColor && onForeground) || (key == KoCanvasResourceProvider::BackgroundColor && onBackground)) { setColor(v.value()); } } void KisMinimalShadeSelector::paintEvent(QPaintEvent *) { QPainter painter(this); painter.fillRect(0,0,width(), height(), QColor(128,128,128)); } KisColorSelectorBase* KisMinimalShadeSelector::createPopup() const { KisMinimalShadeSelector* popup = new KisMinimalShadeSelector(0); popup->setColor(m_lastRealColor); return popup; } diff --git a/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.h b/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.h index 0b86c095e2..2d4aa38ba1 100644 --- a/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.h +++ b/plugins/dockers/advancedcolorselector/kis_minimal_shade_selector.h @@ -1,63 +1,64 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_MINIMAL_SHADE_SELECTOR_H #define KIS_MINIMAL_SHADE_SELECTOR_H #include #include #include "kis_color_selector_base.h" class KisShadeSelectorLine; class KisCanvas2; class KisColorSelectorBaseProxy; class KisMinimalShadeSelector : public KisColorSelectorBase { Q_OBJECT public: explicit KisMinimalShadeSelector(QWidget *parent = 0); ~KisMinimalShadeSelector() override; void unsetCanvas() override; void setCanvas(KisCanvas2* canvas) override; protected: void setColor(const KoColor& color) override; void mouseMoveEvent(QMouseEvent *) override; void mousePressEvent(QMouseEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; public Q_SLOTS: void updateSettings() override; protected Q_SLOTS: void canvasResourceChanged(int key, const QVariant& v) override; protected: void paintEvent(QPaintEvent *) override; KisColorSelectorBase* createPopup() const override; private: QList m_shadingLines; KoColor m_lastRealColor; QPointer m_canvas; QScopedPointer m_proxy; }; #endif diff --git a/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp b/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp index b7083534a3..e5f1f36e55 100644 --- a/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp +++ b/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.cpp @@ -1,303 +1,304 @@ /* * Copyright (c) 2010 Adam Celarek * Copyright (c) 2008 Martin Renold * Copyright (c) 2009 Ilya Portnov * * This class is based on "lib/colorchanger.hpp" from MyPaint (mypaint.intilinux.com) * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_my_paint_shade_selector.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "KoColor.h" #include "KoCanvasResourceProvider.h" #include "kis_paint_device.h" #include "kis_painter.h" #include "kis_display_color_converter.h" inline int sqr(int x); inline qreal sqr2(qreal x); inline int signedSqr(int x); KisMyPaintShadeSelector::KisMyPaintShadeSelector(QWidget *parent) : KisColorSelectorBase(parent), m_updateTimer(new QTimer(this)) { setAcceptDrops(true); updateSettings(); setMinimumSize(80, 80); setColor(KoColor(Qt::red, colorSpace())); m_updateTimer->setInterval(1); m_updateTimer->setSingleShot(true); connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update())); } void KisMyPaintShadeSelector::paintEvent(QPaintEvent *) { // Hint to the casual reader: some of the calculation here do not // what Martin Renold originally intended. Not everything here will make sense. // It does not matter in the end, as long as the result looks good. // This selector was ported from MyPaint in 2010 if (m_cachedColorSpace != colorSpace()) { m_realPixelCache = new KisPaintDevice(colorSpace()); m_realCircleBorder = new KisPaintDevice(colorSpace()); m_cachedColorSpace = colorSpace(); } else { m_realPixelCache->clear(); m_realCircleBorder->clear(); } KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV"); int size = qMin(width(), height()); int s_radius = size/2.6; for (int x=0; x 0) dxs = dx - stripe_width; else dxs = dx + stripe_width; if (dy > 0) dys = dy - stripe_width; else dys = dy + stripe_width; qreal r = std::sqrt(qreal(sqr(dxs)+sqr(dys))); if (qMin(abs(dx), abs(dy)) < stripe_width) { // horizontal and vertical lines dx = (dx/qreal(width()))*255; dy = (dy/qreal(height()))*255; h = 0; // x-axis = value, y-axis = saturation v = dx*v_factor + signedSqr(dx)*v_factor2; s = - (dy*s_factor + signedSqr(dy)*s_factor2); // but not both at once if (std::abs(dx) > std::abs(dy)) { // horizontal stripe s = 0.0; } else { // vertical stripe v = 0.0; } } else if (r < s_radius+1) { // hue if (dx > 0) h = 90*sqr2(r/s_radius); else h = 360 - 90*sqr2(r/s_radius); s = 256*(atan2f(std::abs(dxs),dys)/M_PI) - 128; if (r > s_radius) { // antialiasing boarder qreal aaFactor = r-floor(r); // part after the decimal point aaFactor = 1-aaFactor; qreal fh = m_colorH + h/360.0; qreal fs = m_colorS + s/255.0; qreal fv = m_colorV + v/255.0; fh -= floor(fh); fs = qBound(qreal(0.0), fs, qreal(1.0)); fv = qBound(qreal(0.01), fv, qreal(1.0)); KoColor color; //KoColor color = converter()->fromHsvF(fh, fs, fv); if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);} else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);} else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);} else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv, R, G, B);} else{dbgKrita<<"MyPaint Color selector don't work right."; color = converter()->fromHsvF(fh, fs, fv);} //dbgKrita<toQcolor(); color.setOpacity(aaFactor); Acs::setColor(m_realCircleBorder, QPoint(x, y), color); h = 180 + 180*atan2f(dys,-dxs)/M_PI; v = 255*(r-s_radius)/(diag-s_radius) - 128; } } else { // background (hue+darkness gradient) h = 180 + 180*atan2f(dys,-dxs)/M_PI; v = 255*(r-s_radius)/(diag-s_radius) - 128; } qreal fh = m_colorH + h/360.0; qreal fs = m_colorS + s/255.0; qreal fv = m_colorV + v/255.0; fh -= floor(fh); fs = qBound(qreal(0.0), fs, qreal(1.0)); fv = qBound(qreal(0.01), fv, qreal(1.0)); KoColor color; //KoColor color = converter()->fromHsvF(fh, fs, fv); if(shadeMyPaintType=="HSV"){color = converter()->fromHsvF(fh, fs, fv);} else if(shadeMyPaintType=="HSL"){color = converter()->fromHslF(fh, fs, fv);} else if(shadeMyPaintType=="HSI"){color = converter()->fromHsiF(fh, fs, fv);} else if(shadeMyPaintType=="HSY"){color = converter()->fromHsyF(fh, fs, fv);} else{dbgKrita<<"MyPaint Color selector don't work right."; color = converter()->fromHsvF(fh, fs, fv);} Acs::setColor(m_realPixelCache, QPoint(x, y), color); } } KisPainter gc(m_realPixelCache); gc.bitBlt(QPoint(0,0), m_realCircleBorder, rect()); QPainter painter(this); QImage renderedImage = converter()->toQImage(m_realPixelCache); painter.drawImage(0, 0, renderedImage); } void KisMyPaintShadeSelector::mousePressEvent(QMouseEvent* e) { e->setAccepted(false); KisColorSelectorBase::mousePressEvent(e); } void KisMyPaintShadeSelector::mouseMoveEvent(QMouseEvent *e) { if(rect().contains(e->pos())) { KoColor color(Acs::pickColor(m_realPixelCache, e->pos())); this->updateColorPreview(color); } KisColorSelectorBase::mouseMoveEvent(e); } void KisMyPaintShadeSelector::mouseReleaseEvent(QMouseEvent *e) { e->setAccepted(false); KisColorSelectorBase::mouseReleaseEvent(e); if(!e->isAccepted()) { KoColor color(Acs::pickColor(m_realPixelCache, e->pos())); Acs::ColorRole role = Acs::buttonToRole(e->button()); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); bool onRightClick = cfg.readEntry("shadeSelectorUpdateOnRightClick", false); bool onLeftClick = cfg.readEntry("shadeSelectorUpdateOnLeftClick", false); bool explicitColorReset = (e->button() == Qt::LeftButton && onLeftClick) || (e->button() == Qt::RightButton && onRightClick); this->updateColor(color, role, explicitColorReset); e->accept(); } } KisColorSelectorBase* KisMyPaintShadeSelector::createPopup() const { KisColorSelectorBase* popup = new KisMyPaintShadeSelector(0); popup->setColor(m_lastRealColor); return popup; } void KisMyPaintShadeSelector::setColor(const KoColor &color) { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); QString shadeMyPaintType=cfg.readEntry("shadeMyPaintType", "HSV"); R = cfg.readEntry("lumaR", 0.2126); G = cfg.readEntry("lumaG", 0.7152); B = cfg.readEntry("lumaB", 0.0722); if(shadeMyPaintType=="HSV"){this->converter()->getHsvF(color, &m_colorH, &m_colorS, &m_colorV);} if(shadeMyPaintType=="HSL"){this->converter()->getHslF(color, &m_colorH, &m_colorS, &m_colorV);} if(shadeMyPaintType=="HSI"){this->converter()->getHsiF(color, &m_colorH, &m_colorS, &m_colorV);} if(shadeMyPaintType=="HSY"){this->converter()->getHsyF(color, &m_colorH, &m_colorS, &m_colorV, R, G, B);} m_lastRealColor = color; this->updateColorPreview(color); m_updateTimer->start(); } void KisMyPaintShadeSelector::canvasResourceChanged(int key, const QVariant &v) { if(m_colorUpdateAllowed==false) return; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); bool onForeground = cfg.readEntry("shadeSelectorUpdateOnForeground", false); bool onBackground = cfg.readEntry("shadeSelectorUpdateOnBackground", true); if ((key == KoCanvasResourceProvider::ForegroundColor && onForeground) || (key == KoCanvasResourceProvider::BackgroundColor && onBackground)) { setColor(v.value()); } } inline int sqr(int x) { return x*x; } inline qreal sqr2(qreal x) { return (x*x)/2+x/2; } inline int signedSqr(int x) { int sign = x>0?1:-1; return x*x*sign; } diff --git a/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.h b/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.h index f0e5bab939..319551f460 100644 --- a/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.h +++ b/plugins/dockers/advancedcolorselector/kis_my_paint_shade_selector.h @@ -1,64 +1,65 @@ /* * Copyright (c) 2010 Adam Celarek * Copyright (c) 2008 Martin Renold * Copyright (c) 2009 Ilya Portnov * * This class is based on "lib/colorchanger.hpp" from MyPaint (mypaint.intilinux.com) * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_MY_PAINT_SHADE_SELECTOR_H #define KIS_MY_PAINT_SHADE_SELECTOR_H #include "kis_color_selector_base.h" #include #include #include class KoColorSpace; class QTimer; class KisMyPaintShadeSelector : public KisColorSelectorBase { Q_OBJECT public: KisMyPaintShadeSelector(QWidget *parent = 0); void mousePressEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; public: void setColor(const KoColor &color) override; protected Q_SLOTS: void canvasResourceChanged(int key, const QVariant& v) override; protected: void paintEvent(QPaintEvent *) override; KisColorSelectorBase* createPopup() const override; private: qreal m_colorH, m_colorS, m_colorV; qreal R, G, B; QTimer* m_updateTimer; KoColor m_lastRealColor; KisPaintDeviceSP m_realPixelCache; KisPaintDeviceSP m_realCircleBorder; const KoColorSpace *m_cachedColorSpace; }; #endif // KIS_MY_PAINT_SHADE_SELECTOR_H diff --git a/plugins/dockers/advancedcolorselector/kis_shade_selector_line.cpp b/plugins/dockers/advancedcolorselector/kis_shade_selector_line.cpp index fa743d6bfd..03fa380c66 100644 --- a/plugins/dockers/advancedcolorselector/kis_shade_selector_line.cpp +++ b/plugins/dockers/advancedcolorselector/kis_shade_selector_line.cpp @@ -1,275 +1,276 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_shade_selector_line.h" #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "kis_color_selector_base_proxy.h" #include "kis_display_color_converter.h" #include "kis_paint_device.h" KisShadeSelectorLine::KisShadeSelectorLine(KisColorSelectorBaseProxy *parentProxy, QWidget *parent) : KisShadeSelectorLineBase(parent), m_cachedColorSpace(0), m_displayHelpText(false), m_parentProxy(parentProxy) { setParam(0, 0, 0, 0, 0, 0); updateSettings(); setMouseTracking(true); m_mouseX=width()/2; m_isDown=false; } KisShadeSelectorLine::KisShadeSelectorLine(qreal hueDelta, qreal satDelta, qreal valDelta, KisColorSelectorBaseProxy *parentProxy, QWidget *parent, qreal hueShift, qreal satShift, qreal valShift) : KisShadeSelectorLineBase(parent), m_cachedColorSpace(0), m_displayHelpText(false), m_parentProxy(parentProxy) { setParam(hueDelta, satDelta, valDelta, hueShift, satShift, valShift); updateSettings(); m_mouseX=width()/2; m_isDown=false; } KisShadeSelectorLine::~KisShadeSelectorLine() { } void KisShadeSelectorLine::setParam(qreal hueDelta, qreal satDelta, qreal valDelta, qreal hueShift, qreal satShift, qreal valShift) { m_hueDelta = hueDelta; m_saturationDelta = satDelta; m_valueDelta = valDelta; m_hueShift = hueShift; m_saturationShift = satShift; m_valueShift = valShift; } void KisShadeSelectorLine::setColor(const KoColor &color) { m_realColor = color; m_realColor.convertTo(m_parentProxy->colorSpace()); m_mouseX=width()/2; update(); } void KisShadeSelectorLine::updateSettings() { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); m_gradient = cfg.readEntry("minimalShadeSelectorAsGradient", false); m_patchCount = cfg.readEntry("minimalShadeSelectorPatchCount", 10); m_lineHeight = cfg.readEntry("minimalShadeSelectorLineHeight", 20); setMaximumHeight(m_lineHeight); setMinimumHeight(m_lineHeight); } QString KisShadeSelectorLine::toString() const { return QString("%1|%2|%3|%4|%5|%6|%7").arg(m_lineNumber).arg(m_hueDelta).arg(m_saturationDelta).arg(m_valueDelta).arg(m_hueShift).arg(m_saturationShift).arg(m_valueShift); } void KisShadeSelectorLine::fromString(const QString& string) { QStringList strili = string.split('|'); m_lineNumber = strili.at(0).toInt(); m_hueDelta = strili.at(1).toDouble(); m_saturationDelta = strili.at(2).toDouble(); m_valueDelta = strili.at(3).toDouble(); if(strili.size()==4) return; // don't crash, if reading old config files. m_hueShift = strili.at(4).toDouble(); m_saturationShift = strili.at(5).toDouble(); m_valueShift = strili.at(6).toDouble(); } void KisShadeSelectorLine::paintEvent(QPaintEvent *) { if (m_cachedColorSpace != m_parentProxy->colorSpace()) { m_realPixelCache = new KisPaintDevice(m_parentProxy->colorSpace()); m_cachedColorSpace = m_parentProxy->colorSpace(); } else { m_realPixelCache->clear(); } int patchCount; int patchSpacing; if(m_gradient) { patchCount = width(); patchSpacing = 0; } else { patchCount = m_patchCount; patchSpacing = 3; } qreal patchWidth = (width()-patchSpacing*patchCount)/qreal(patchCount); qreal hueStep; qreal saturationStep; qreal valueStep; if(m_gradient){ hueStep=m_hueDelta/qreal(patchCount-10); saturationStep=m_saturationDelta/qreal(patchCount-10); valueStep=m_valueDelta/qreal(patchCount-10); } else{ hueStep=m_hueDelta/qreal(patchCount); saturationStep=m_saturationDelta/qreal(patchCount); valueStep=m_valueDelta/qreal(patchCount); } qreal baseHue; qreal baseSaturation; qreal baseValue; m_parentProxy->converter()-> getHsvF(m_realColor, &baseHue, &baseSaturation, &baseValue); int z=0; for(int i=-patchCount/2; i<=patchCount/2; i++) { if(i==0 && patchCount%2==0) continue; qreal hue = baseHue + (i * hueStep) + m_hueShift; while (hue < 0.0) hue += 1.0; while (hue > 1.0) hue -= 1.0; qreal saturation = qBound(0., baseSaturation + (i * saturationStep) + m_saturationShift, 1.); qreal value = qBound(0., baseValue + (i * valueStep) + m_valueShift, 1.); if (qAbs(i) <= 5 && m_gradient) { hue = baseHue; saturation=baseSaturation; value=baseValue; } QRect patchRect(z * (patchWidth + patchSpacing), 0, patchWidth, m_lineHeight); KoColor patchColor = m_parentProxy->converter()->fromHsvF(hue, saturation, value); patchColor.convertTo(m_realPixelCache->colorSpace()); m_realPixelCache->fill(patchRect, patchColor); z++; } QPainter wpainter(this); QImage renderedImage = m_parentProxy->converter()->toQImage(m_realPixelCache); wpainter.drawImage(0, 0, renderedImage); if (m_gradient) { wpainter.setPen(QColor(175,175,175)); wpainter.drawRect(renderedImage.width()/2-5,0,10,renderedImage.height()-1); wpainter.setPen(QColor(75,75,75)); wpainter.drawRect(renderedImage.width()/2-4,0,8,renderedImage.height()-1); wpainter.setPen(QColor(175,175,175)); qreal mouseX = (qBound(5.0,m_mouseX,m_width-5)); wpainter.drawRect(mouseX-5,0,10,renderedImage.height()-1); wpainter.setPen(QColor(75,75,75)); wpainter.drawRect(mouseX-4,0,8,renderedImage.height()-1); } m_width=width(); if(m_displayHelpText) { QString helpText(i18n("delta h=%1 s=%2 v=%3 shift h=%4 s=%5 v=%6", m_hueDelta, m_saturationDelta, m_valueDelta, m_hueShift, m_saturationShift, m_valueShift)); wpainter.setPen(QColor(255,255,255)); wpainter.drawText(rect(), helpText); } } void KisShadeSelectorLine::resizeEvent(QResizeEvent *e) { QSize m_old = e->oldSize(); QSize m_new = e->size(); int m_nWidth = m_new.width(); int m_oWidth = m_old.width(); m_width = width(); m_mouseX = m_mouseX * m_nWidth / m_oWidth; } void KisShadeSelectorLine::mousePressEvent(QMouseEvent* e) { if(e->button()!=Qt::LeftButton && e->button()!=Qt::RightButton) { e->setAccepted(false); return; } if (e->y() > 0 && e->y() < height()) { m_parentProxy->showColorPreview(); e->accept(); m_mouseX = e->x(); m_isDown=true; update(); } } void KisShadeSelectorLine::mouseMoveEvent(QMouseEvent *e) { if ((m_isDown) && (e->buttons() & Qt::LeftButton)) { m_mouseX=e->x(); const QPoint mouseEv ((qBound(5.0,m_mouseX,m_width-5)),5); KoColor color(Acs::pickColor(m_realPixelCache, mouseEv)); m_parentProxy->updateColorPreview(color); update(); } } void KisShadeSelectorLine::mouseReleaseEvent(QMouseEvent * e) { if (e->button() != Qt::LeftButton && e->button() != Qt::RightButton) { e->ignore(); return; } m_mouseX=e->x(); const QPoint mouseEv ((qBound(5.0,m_mouseX,m_width-5)),5); KoColor color(Acs::pickColor(m_realPixelCache, mouseEv)); m_parentProxy->updateColorPreview(color); Acs::ColorRole role = Acs::buttonToRole(e->button()); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); bool onRightClick = cfg.readEntry("shadeSelectorUpdateOnRightClick", false); bool onLeftClick = cfg.readEntry("shadeSelectorUpdateOnLeftClick", false); bool explicitColorReset = (e->button() == Qt::LeftButton && onLeftClick) || (e->button() == Qt::RightButton && onRightClick); m_parentProxy->updateColor(color, role, explicitColorReset); e->accept(); m_isDown=false; } diff --git a/plugins/dockers/advancedcolorselector/kis_shade_selector_line.h b/plugins/dockers/advancedcolorselector/kis_shade_selector_line.h index 2ac5ec8c7a..508d327672 100644 --- a/plugins/dockers/advancedcolorselector/kis_shade_selector_line.h +++ b/plugins/dockers/advancedcolorselector/kis_shade_selector_line.h @@ -1,96 +1,97 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_SHADE_SELECTOR_LINE_H #define KIS_SHADE_SELECTOR_LINE_H #include #include #include "kis_types.h" class KisCanvas2; class KisShadeSelectorLineComboBox; class KisColorSelectorBaseProxy; class KoColorSpace; class KisShadeSelectorLineBase : public QWidget { public: KisShadeSelectorLineBase(QWidget* parent) : QWidget(parent) {} void setLineNumber(int n) {m_lineNumber=n;} virtual QString toString() const = 0; virtual void fromString(const QString& string) = 0; protected: int m_lineNumber; }; class KisShadeSelectorLine : public KisShadeSelectorLineBase { Q_OBJECT public: explicit KisShadeSelectorLine(KisColorSelectorBaseProxy *parentProxy, QWidget *parent = 0); explicit KisShadeSelectorLine(qreal hueDelta, qreal satDelta, qreal valDelta, KisColorSelectorBaseProxy *parentProxy, QWidget *parent = 0, qreal hueShift = 0, qreal satShift = 0, qreal valShift = 0); ~KisShadeSelectorLine() override; void setParam(qreal hue, qreal sat, qreal val, qreal hueShift, qreal satShift, qreal shiftVal); void setColor(const KoColor& color); void updateSettings(); void setCanvas(KisCanvas2* canvas); void showHelpText() {m_displayHelpText=true;} QString toString() const override; void fromString(const QString& string) override; void paintEvent(QPaintEvent *) override; void resizeEvent(QResizeEvent *) override; void mousePressEvent(QMouseEvent *) override; void mouseMoveEvent(QMouseEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; private: qreal m_hueDelta; qreal m_saturationDelta; qreal m_valueDelta; qreal m_hueShift; qreal m_saturationShift; qreal m_valueShift; KoColor m_realColor; KisPaintDeviceSP m_realPixelCache; const KoColorSpace *m_cachedColorSpace; bool m_gradient; int m_patchCount; int m_lineHeight; bool m_displayHelpText; qreal m_mouseX; QPoint m_ev; qreal m_width; bool m_isDown; friend class KisShadeSelectorLineComboBox; KisColorSelectorBaseProxy* m_parentProxy; }; #endif // KIS_SHADE_SELECTOR_LINE_H diff --git a/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.cpp b/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.cpp index 1e2ecb2367..f43ece889c 100644 --- a/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.cpp +++ b/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.cpp @@ -1,173 +1,174 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_shade_selector_line_combo_box.h" #include #include #include #include #include #include "kis_shade_selector_line.h" #include "kis_shade_selector_line_combo_box_popup.h" #include "kis_color_selector_base_proxy.h" #include "kis_global.h" KisShadeSelectorLineComboBox::KisShadeSelectorLineComboBox(QWidget *parent) : QComboBox(parent), m_popup(new KisShadeSelectorLineComboBoxPopup(this)), m_parentProxy(new KisColorSelectorBaseProxyNoop()), m_currentLine(new KisShadeSelectorLine(0,0,0, m_parentProxy.data(), this)) { QGridLayout* l = new QGridLayout(this); l->addWidget(m_currentLine); m_currentLine->setEnabled(false); KoColor color; color.fromQColor(QColor(190, 50, 50)); m_currentLine->setColor(color); updateSettings(); } KisShadeSelectorLineComboBox::~KisShadeSelectorLineComboBox() { } void KisShadeSelectorLineComboBox::hidePopup() { QComboBox::hidePopup(); m_popup->hide(); } void KisShadeSelectorLineComboBox::showPopup() { QComboBox::showPopup(); m_popup->show(); const int widgetMargin = 20; const QRect fitRect = kisGrowRect(QApplication::desktop()->screenGeometry(), -widgetMargin); QRect popupRect = m_popup->rect(); popupRect.moveTo(mapToGlobal(QPoint())); popupRect = kisEnsureInRect(popupRect, fitRect); m_popup->move(popupRect.topLeft()); m_popup->setConfiguration(m_currentLine->toString()); } void KisShadeSelectorLineComboBox::setConfiguration(const QString &stri) { m_currentLine->fromString(stri); update(); } QString KisShadeSelectorLineComboBox::configuration() const { return m_currentLine->toString(); } void KisShadeSelectorLineComboBox::setLineNumber(int n) { m_currentLine->setLineNumber(n); for(int i=0; ilayout()->count(); i++) { KisShadeSelectorLine* item = dynamic_cast(m_popup->layout()->itemAt(i)->widget()); if(item!=0) { item->setLineNumber(n); } } } void KisShadeSelectorLineComboBox::resizeEvent(QResizeEvent *e) { Q_UNUSED(e); m_currentLine->setMaximumWidth(width()-30-m_popup->spacing); m_popup->setMinimumWidth(qMax(280, width())); m_popup->setMaximumWidth(qMax(280, width())); } void KisShadeSelectorLineComboBox::updateSettings() { m_currentLine->updateSettings(); for(int i=0; ilayout()->count(); i++) { KisShadeSelectorLine* item = dynamic_cast(m_popup->layout()->itemAt(i)->widget()); if(item!=0) { item->updateSettings(); item->m_lineHeight=30; item->setMaximumHeight(30); item->setMinimumHeight(30); } } setLineHeight(m_currentLine->m_lineHeight); } void KisShadeSelectorLineComboBox::setGradient(bool b) { m_currentLine->m_gradient=b; for(int i=0; ilayout()->count(); i++) { KisShadeSelectorLine* item = dynamic_cast(m_popup->layout()->itemAt(i)->widget()); if(item!=0) { item->m_gradient=b; } } update(); } void KisShadeSelectorLineComboBox::setPatches(bool b) { m_currentLine->m_gradient=!b; for(int i=0; ilayout()->count(); i++) { KisShadeSelectorLine* item = dynamic_cast(m_popup->layout()->itemAt(i)->widget()); if(item!=0) { item->m_gradient=!b; } } update(); } void KisShadeSelectorLineComboBox::setPatchCount(int count) { m_currentLine->m_patchCount=count; for(int i=0; ilayout()->count(); i++) { KisShadeSelectorLine* item = dynamic_cast(m_popup->layout()->itemAt(i)->widget()); if(item!=0) { item->m_patchCount=count; } } update(); } void KisShadeSelectorLineComboBox::setLineHeight(int height) { m_currentLine->m_lineHeight=height; m_currentLine->setMinimumHeight(height); m_currentLine->setMaximumHeight(height); setMinimumHeight(height+m_popup->spacing); setMaximumHeight(height+m_popup->spacing); update(); } diff --git a/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.h b/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.h index 72e069a631..66f97ec525 100644 --- a/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.h +++ b/plugins/dockers/advancedcolorselector/kis_shade_selector_line_combo_box.h @@ -1,56 +1,57 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_SHADE_SELECTOR_LINE_COMBO_BOX_H #define KIS_SHADE_SELECTOR_LINE_COMBO_BOX_H #include class KisShadeSelectorLineComboBoxPopup; class KisShadeSelectorLine; class KisColorSelectorBaseProxy; class KisShadeSelectorLineComboBox : public QComboBox { Q_OBJECT public: explicit KisShadeSelectorLineComboBox(QWidget *parent = 0); ~KisShadeSelectorLineComboBox() override; void hidePopup() override; void showPopup() override; QString configuration() const; void setLineNumber(int n); protected: void resizeEvent(QResizeEvent *e) override; public Q_SLOTS: void setConfiguration(const QString& stri); void updateSettings(); void setGradient(bool); void setPatches(bool); void setPatchCount(int count); void setLineHeight(int height); private: KisShadeSelectorLineComboBoxPopup* m_popup; QScopedPointer m_parentProxy; KisShadeSelectorLine* m_currentLine; }; #endif // KIS_SHADE_SELECTOR_LINE_COMBO_BOX_H diff --git a/plugins/dockers/advancedcolorselector/kis_shade_selector_lines_settings.cpp b/plugins/dockers/advancedcolorselector/kis_shade_selector_lines_settings.cpp index b998933527..81d173321d 100644 --- a/plugins/dockers/advancedcolorselector/kis_shade_selector_lines_settings.cpp +++ b/plugins/dockers/advancedcolorselector/kis_shade_selector_lines_settings.cpp @@ -1,88 +1,89 @@ /* * Copyright (c) 2010 Adam Celarek * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_shade_selector_lines_settings.h" #include #include #include #include #include "kis_shade_selector_line_combo_box.h" KisShadeSelectorLinesSettings::KisShadeSelectorLinesSettings(QWidget *parent) : QWidget(parent) { QVBoxLayout* l = new QVBoxLayout(this); l->setSpacing(0); l->setMargin(0); } QString KisShadeSelectorLinesSettings::toString() const { QString result; for(int i=0; iconfiguration()); result.append(';'); } return result; } void KisShadeSelectorLinesSettings::fromString(const QString &stri) { QStringList strili = stri.split(';', QString::SkipEmptyParts); setLineCount(strili.size()); for(int i=0; isetConfiguration(strili.at(i)); } } void KisShadeSelectorLinesSettings::updateSettings() { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); fromString(cfg.readEntry("minimalShadeSelectorLineConfig", "0|0.2|0|0")); for(int i=0; iupdateSettings(); } } void KisShadeSelectorLinesSettings::setLineCount(int count) { bool emitSignal = (m_lineList.size()!=count)?true:false; while(count-m_lineList.size() > 0) { m_lineList.append(new KisShadeSelectorLineComboBox(this)); m_lineList.last()->setLineNumber(m_lineList.size()-1); layout()->addWidget(m_lineList.last()); } while(count-m_lineList.size() < 0) { layout()->removeWidget(m_lineList.last()); delete m_lineList.takeLast(); } for(int i=0; i * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_SHADE_SELECTOR_LINES_SETTINGS_H #define KIS_SHADE_SELECTOR_LINES_SETTINGS_H #include class KisShadeSelectorLineComboBox; class KisShadeSelectorLinesSettings : public QWidget { Q_OBJECT public: explicit KisShadeSelectorLinesSettings(QWidget *parent = 0); QString toString() const; void fromString(const QString& stri); public Q_SLOTS: void updateSettings(); void setLineCount(int count); Q_SIGNALS: void setGradient(bool); void setPatches(bool); void setPatchCount(int count); void setLineHeight(int height); Q_SIGNALS: void lineCountChanged(int newLineCount); private: QList m_lineList; }; #endif // KIS_SHADE_SELECTOR_LINES_SETTINGS_H diff --git a/plugins/dockers/arrangedocker/arrange_docker_widget.cpp b/plugins/dockers/arrangedocker/arrange_docker_widget.cpp index 760ff036da..bd133d385b 100644 --- a/plugins/dockers/arrangedocker/arrange_docker_widget.cpp +++ b/plugins/dockers/arrangedocker/arrange_docker_widget.cpp @@ -1,102 +1,103 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "arrange_docker_widget.h" #include "ui_arrange_docker_widget.h" #include "kis_debug.h" #include "kactioncollection.h" #include #include struct ArrangeDockerWidget::Private { }; ArrangeDockerWidget::ArrangeDockerWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ArrangeDockerWidget), m_d(new Private) { ui->setupUi(this); } ArrangeDockerWidget::~ArrangeDockerWidget() { } void replaceAction(QToolButton *button, QAction *newAction) { Q_FOREACH (QAction *action, button->actions()) { button->removeAction(action); } if (newAction) { button->setDefaultAction(newAction); } } void ArrangeDockerWidget::setActionCollection(KActionCollection *collection) { const bool enabled = collection->action("object_order_front"); if (enabled) { replaceAction(ui->bringToFront, collection->action("object_order_front")); replaceAction(ui->raiseLevel, collection->action("object_order_raise")); replaceAction(ui->lowerLevel, collection->action("object_order_lower")); replaceAction(ui->sendBack, collection->action("object_order_back")); replaceAction(ui->leftAlign, collection->action("object_align_horizontal_left")); replaceAction(ui->hCenterAlign, collection->action("object_align_horizontal_center")); replaceAction(ui->rightAlign, collection->action("object_align_horizontal_right")); replaceAction(ui->topAlign, collection->action("object_align_vertical_top")); replaceAction(ui->vCenterAlign, collection->action("object_align_vertical_center")); replaceAction(ui->bottomAlign, collection->action("object_align_vertical_bottom")); replaceAction(ui->hDistributeLeft, collection->action("object_distribute_horizontal_left")); replaceAction(ui->hDistributeCenter, collection->action("object_distribute_horizontal_center")); replaceAction(ui->hDistributeRight, collection->action("object_distribute_horizontal_right")); replaceAction(ui->hDistributeGaps, collection->action("object_distribute_horizontal_gaps")); replaceAction(ui->vDistributeTop, collection->action("object_distribute_vertical_top")); replaceAction(ui->vDistributeCenter, collection->action("object_distribute_vertical_center")); replaceAction(ui->vDistributeBottom, collection->action("object_distribute_vertical_bottom")); replaceAction(ui->vDistributeGaps, collection->action("object_distribute_vertical_gaps")); replaceAction(ui->group, collection->action("object_group")); replaceAction(ui->ungroup, collection->action("object_ungroup")); } setEnabled(enabled); } void ArrangeDockerWidget::switchState(bool enabled) { if (enabled) { ui->buttons->show(); ui->disabledLabel->hide(); } else { ui->buttons->hide(); ui->disabledLabel->show(); } } diff --git a/plugins/dockers/arrangedocker/arrange_docker_widget.h b/plugins/dockers/arrangedocker/arrange_docker_widget.h index 4054dbc5b9..17768cd0e7 100644 --- a/plugins/dockers/arrangedocker/arrange_docker_widget.h +++ b/plugins/dockers/arrangedocker/arrange_docker_widget.h @@ -1,49 +1,50 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ARRANGE_DOCKER_WIDGET_H #define ARRANGE_DOCKER_WIDGET_H #include #include #include "kactioncollection.h" namespace Ui { class ArrangeDockerWidget; } class ArrangeDockerWidget : public QWidget { Q_OBJECT public: explicit ArrangeDockerWidget(QWidget *parent = 0); ~ArrangeDockerWidget() override; void setActionCollection(KActionCollection *collection); void switchState(bool enabled); private: Ui::ArrangeDockerWidget *ui; struct Private; const QScopedPointer m_d; }; #endif // ARRANGE_DOCKER_WIDGET_H diff --git a/plugins/dockers/arrangedocker/arrangedocker.cpp b/plugins/dockers/arrangedocker/arrangedocker.cpp index 63f5929832..b1fbb406cd 100644 --- a/plugins/dockers/arrangedocker/arrangedocker.cpp +++ b/plugins/dockers/arrangedocker/arrangedocker.cpp @@ -1,77 +1,78 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "arrangedocker.h" #include #include #include #include #include #include "KisViewManager.h" #include "arrangedocker_dock.h" K_PLUGIN_FACTORY_WITH_JSON(ArrangeDockerPluginFactory, "krita_arrangedocker.json", registerPlugin();) class ArrangeDockerDockFactory : public KoDockFactoryBase { public: ArrangeDockerDockFactory() { } QString id() const override { return QString( "ArrangeDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { ArrangeDockerDock * dockWidget = new ArrangeDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; ArrangeDockerPlugin::ArrangeDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new ArrangeDockerDockFactory()); } ArrangeDockerPlugin::~ArrangeDockerPlugin() { m_view = 0; } #include "arrangedocker.moc" diff --git a/plugins/dockers/arrangedocker/arrangedocker.h b/plugins/dockers/arrangedocker/arrangedocker.h index 3e1880a238..dde0404187 100644 --- a/plugins/dockers/arrangedocker/arrangedocker.h +++ b/plugins/dockers/arrangedocker/arrangedocker.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ARRANGE_DOCKER_H_ #define _ARRANGE_DOCKER_H_ #include #include class KisViewManager; /** * Template of view plugin */ class ArrangeDockerPlugin : public QObject { Q_OBJECT public: ArrangeDockerPlugin(QObject *parent, const QVariantList &); ~ArrangeDockerPlugin() override; private: KisViewManager* m_view; }; #endif diff --git a/plugins/dockers/arrangedocker/arrangedocker_dock.cpp b/plugins/dockers/arrangedocker/arrangedocker_dock.cpp index 0208d8e25c..32c11c305f 100644 --- a/plugins/dockers/arrangedocker/arrangedocker_dock.cpp +++ b/plugins/dockers/arrangedocker/arrangedocker_dock.cpp @@ -1,90 +1,91 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "arrangedocker_dock.h" #include #include "kis_canvas2.h" #include #include "arrange_docker_widget.h" #include #include ArrangeDockerDock::ArrangeDockerDock( ) : QDockWidget(i18n("Arrange")) , m_canvas(0) { m_configWidget = new ArrangeDockerWidget(this); m_configWidget->switchState(false); setWidget(m_configWidget); setEnabled(m_canvas); } ArrangeDockerDock::~ArrangeDockerDock() { } void ArrangeDockerDock::setCanvas(KoCanvasBase * canvas) { if(canvas && m_canvas == canvas) return; if (m_canvas) { m_canvasConnections.clear(); m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } m_canvas = canvas ? dynamic_cast(canvas) : 0; setEnabled(m_canvas); if (m_canvas) { m_canvasConnections.addConnection( m_canvas->toolProxy(), SIGNAL(toolChanged(QString)), this, SLOT(slotToolChanged(QString))); m_canvasConnections.addConnection( m_canvas->shapeManager(), SIGNAL(selectionChanged()), this, SLOT(slotToolChanged())); slotToolChanged(); } } void ArrangeDockerDock::unsetCanvas() { setCanvas(0); } void ArrangeDockerDock::slotToolChanged() { KActionCollection *collection = m_canvas->viewManager()->actionCollection(); m_configWidget->setActionCollection(collection); } void ArrangeDockerDock::slotToolChanged(QString toolId) { bool enableWidget = (toolId == "InteractionTool") ? true : false; m_configWidget->switchState(enableWidget); slotToolChanged(); } diff --git a/plugins/dockers/arrangedocker/arrangedocker_dock.h b/plugins/dockers/arrangedocker/arrangedocker_dock.h index ffcb2eac08..ff03550cd2 100644 --- a/plugins/dockers/arrangedocker/arrangedocker_dock.h +++ b/plugins/dockers/arrangedocker/arrangedocker_dock.h @@ -1,49 +1,50 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GRID_DOCK_H_ #define _GRID_DOCK_H_ #include #include #include "kis_signal_auto_connection.h" class KisCanvas2; class ArrangeDockerWidget; class KisSignalAutoConnection; class ArrangeDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: ArrangeDockerDock(); ~ArrangeDockerDock() override; QString observerName() override { return "ArrangeDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; private Q_SLOTS: void slotToolChanged(); void slotToolChanged(QString toolId); private: ArrangeDockerWidget *m_configWidget; QPointer m_canvas; KisSignalAutoConnectionsStore m_canvasConnections; }; #endif diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp index 4ed06991f1..f616e2b480 100644 --- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp +++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.cpp @@ -1,458 +1,459 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "artisticcolorselector_dock.h" #include #include #include #include #include "ui_wdgArtisticColorSelector.h" #include "ui_wdgARCSSettings.h" #include "ui_wdgWheelPreferencesPopup.h" class KisMainWindow; struct ArtisticColorSelectorUI: public QWidget, public Ui_wdgArtisticColorSelector { ArtisticColorSelectorUI() { setupUi(this); } }; struct ARCSSettingsUI: public QWidget, public Ui_wdgARCSSettings { ARCSSettingsUI() { setupUi(this); } }; struct WheelPreferencesPopupUI: public QWidget, public Ui_wdgWheelPreferencesPopup { WheelPreferencesPopupUI() { setupUi(this); } }; ArtisticColorSelectorDock::ArtisticColorSelectorDock() : QDockWidget(i18n("Artistic Color Selector")) , m_canvas(nullptr) , m_resourceProvider(0) , m_selectedMask(nullptr) { setEnabled(false); m_hsxButtons = new QButtonGroup(); m_preferencesUI = new ARCSSettingsUI(); m_wheelPrefsUI = new WheelPreferencesPopupUI(); m_selectorUI = new ArtisticColorSelectorUI(); QPixmap hueStepsPixmap = KisIconUtils::loadIcon("wheel-sectors").pixmap(16,16); QPixmap saturationStepsPixmap = KisIconUtils::loadIcon("wheel-rings").pixmap(16,16); QPixmap valueScaleStepsPixmap = KisIconUtils::loadIcon("wheel-light").pixmap(16,16); QIcon infinityIcon = KisIconUtils::loadIcon("infinity"); m_infinityPixmap = infinityIcon.pixmap(16,16); m_selectorUI->colorSelector->loadSettings(); m_selectorUI->bnWheelPrefs->setIcon(KisIconUtils::loadIcon("wheel-sectors")); m_selectorUI->bnWheelPrefs->setPopupWidget(m_wheelPrefsUI); m_selectorUI->bnDockerPrefs->setPopupWidget(m_preferencesUI); m_selectorUI->bnDockerPrefs->setIcon(KisIconUtils::loadIcon("configure")); //preferences m_hsxButtons->addButton(m_preferencesUI->bnHsy, KisColor::HSY); m_hsxButtons->addButton(m_preferencesUI->bnHsi, KisColor::HSI); m_hsxButtons->addButton(m_preferencesUI->bnHsl, KisColor::HSL); m_hsxButtons->addButton(m_preferencesUI->bnHsv, KisColor::HSV); m_wheelPrefsUI->bnInverseSat->setChecked(m_selectorUI->colorSelector->isSaturationInverted()); m_wheelPrefsUI->labelHueSteps->setPixmap(hueStepsPixmap); m_wheelPrefsUI->labelSaturationSteps->setPixmap(saturationStepsPixmap); m_wheelPrefsUI->labelValueScaleSteps->setPixmap(valueScaleStepsPixmap); m_wheelPrefsUI->numHueSteps->setRange(MIN_NUM_UI_HUE_PIECES, MAX_NUM_HUE_PIECES); m_wheelPrefsUI->numSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS); m_wheelPrefsUI->numValueScaleSteps->setRange(MIN_NUM_UI_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES); m_wheelPrefsUI->bnInfHueSteps->setIcon(infinityIcon); m_wheelPrefsUI->bnInfValueScaleSteps->setIcon(infinityIcon); m_wheelPrefsUI->bnInfHueSteps->setToolTip(i18n("Continuous Mode")); m_wheelPrefsUI->bnInfValueScaleSteps->setToolTip(i18n("Continuous Mode")); int selectorHueSteps = m_selectorUI->colorSelector->getNumPieces(); if (selectorHueSteps == 1) { m_wheelPrefsUI->bnInfHueSteps->setChecked(true); } else { m_wheelPrefsUI->bnInfHueSteps->setChecked(false); } m_wheelPrefsUI->numHueSteps->setValue(selectorHueSteps); m_wheelPrefsUI->numSaturationSteps->setValue(m_selectorUI->colorSelector->getNumRings()); int selectorValueScaleSteps = m_selectorUI->colorSelector->getNumLightPieces(); if (selectorValueScaleSteps == 1) { m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true); } else { m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false); } m_wheelPrefsUI->numValueScaleSteps->setValue(m_selectorUI->colorSelector->getNumLightPieces()); m_preferencesUI->bnDefInfHueSteps->setIcon(infinityIcon); m_preferencesUI->bnDefInfValueScaleSteps->setIcon(infinityIcon); m_preferencesUI->labelDefHueSteps->setPixmap(hueStepsPixmap); m_preferencesUI->labelDefSaturationSteps->setPixmap(saturationStepsPixmap); m_preferencesUI->labelDefValueScaleSteps->setPixmap(valueScaleStepsPixmap); m_preferencesUI->defaultHueSteps->setRange(MIN_NUM_HUE_PIECES, MAX_NUM_HUE_PIECES); m_preferencesUI->defaultSaturationSteps->setRange(MIN_NUM_SATURATION_RINGS, MAX_NUM_SATURATION_RINGS); m_preferencesUI->defaultValueScaleSteps->setRange(MIN_NUM_LIGHT_PIECES, MAX_NUM_LIGHT_PIECES); m_preferencesUI->defaultHueSteps->setValue(m_selectorUI->colorSelector->getDefaultHueSteps()); m_preferencesUI->defaultSaturationSteps->setValue(m_selectorUI->colorSelector->getDefaultSaturationSteps()); m_preferencesUI->defaultValueScaleSteps->setValue(m_selectorUI->colorSelector->getDefaultValueScaleSteps()); m_preferencesUI->showBgColor->setChecked(m_selectorUI->colorSelector->getShowBgColor()); m_preferencesUI->showValueScaleNumbers->setChecked(m_selectorUI->colorSelector->getShowValueScaleNumbers()); m_preferencesUI->enforceGamutMask->setChecked(m_selectorUI->colorSelector->enforceGamutMask()); m_preferencesUI->permissiveGamutMask->setChecked(!m_selectorUI->colorSelector->enforceGamutMask()); m_preferencesUI->spLumaR->setValue(m_selectorUI->colorSelector->lumaR()); m_preferencesUI->spLumaG->setValue(m_selectorUI->colorSelector->lumaG()); m_preferencesUI->spLumaB->setValue(m_selectorUI->colorSelector->lumaB()); m_preferencesUI->spLumaGamma->setValue(m_selectorUI->colorSelector->lumaGamma()); switch(m_selectorUI->colorSelector->getColorSpace()) { case KisColor::HSV: { m_preferencesUI->bnHsv->setChecked(true); } break; case KisColor::HSI: { m_preferencesUI->bnHsi->setChecked(true); } break; case KisColor::HSL: { m_preferencesUI->bnHsl->setChecked(true); } break; case KisColor::HSY: { m_preferencesUI->bnHsy->setChecked(true); } break; } if (m_selectorUI->colorSelector->getColorSpace() == KisColor::HSY) { m_preferencesUI->lumaCoefficientBox->show(); } else { m_preferencesUI->lumaCoefficientBox->hide(); } connect(m_wheelPrefsUI->numValueScaleSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->numHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->numSaturationSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnInverseSat , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_wheelPrefsUI->bnDefault , SIGNAL(clicked(bool)) , SLOT(slotResetDefaultSettings())); connect(m_preferencesUI->defaultHueSteps , SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->defaultSaturationSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->defaultValueScaleSteps, SIGNAL(valueChanged(int)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->bnDefInfHueSteps , SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->bnDefInfValueScaleSteps, SIGNAL(clicked(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->showBgColor , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->showValueScaleNumbers, SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->enforceGamutMask , SIGNAL(toggled(bool)) , SLOT(slotPreferenceChanged())); connect(m_preferencesUI->spLumaR , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_preferencesUI->spLumaG , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_preferencesUI->spLumaB , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_preferencesUI->spLumaGamma , SIGNAL(valueChanged(qreal)), SLOT(slotColorSpaceSelected())); connect(m_selectorUI->colorSelector , SIGNAL(sigFgColorChanged(KisColor)) , SLOT(slotFgColorChanged(KisColor))); connect(m_selectorUI->colorSelector , SIGNAL(sigBgColorChanged(KisColor)) , SLOT(slotBgColorChanged(KisColor))); // gamut mask connections connect(m_selectorUI->gamutMaskToolbar, SIGNAL(sigGamutMaskToggle(bool)), SLOT(slotGamutMaskToggle(bool))); connect(m_hsxButtons , SIGNAL(buttonClicked(int)) , SLOT(slotColorSpaceSelected())); setWidget(m_selectorUI); } ArtisticColorSelectorDock::~ArtisticColorSelectorDock() { m_selectorUI->colorSelector->saveSettings(); delete m_hsxButtons; } void ArtisticColorSelectorDock::setViewManager(KisViewManager* kisview) { m_resourceProvider = kisview->canvasResourceProvider(); m_selectorUI->colorSelector->setFgColor(m_resourceProvider->resourceManager()->foregroundColor()); m_selectorUI->colorSelector->setBgColor(m_resourceProvider->resourceManager()->backgroundColor()); connect(m_resourceProvider, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)), this, SLOT(slotGamutMaskSet(KoGamutMaskSP)), Qt::UniqueConnection); connect(m_resourceProvider, SIGNAL(sigGamutMaskUnset()), this, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection); connect(m_resourceProvider, SIGNAL(sigGamutMaskPreviewUpdate()), this, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection); m_selectorUI->gamutMaskToolbar->connectMaskSignals(m_resourceProvider); } void ArtisticColorSelectorDock::slotCanvasResourceChanged(int key, const QVariant& value) { if(key == KoCanvasResourceProvider::ForegroundColor) m_selectorUI->colorSelector->setFgColor(value.value()); if(key == KoCanvasResourceProvider::BackgroundColor) m_selectorUI->colorSelector->setBgColor(value.value()); } void ArtisticColorSelectorDock::slotFgColorChanged(const KisColor& color) { m_resourceProvider->resourceManager()->setForegroundColor( KoColor(color.toKoColor(), m_resourceProvider->resourceManager()->foregroundColor().colorSpace()) ); } void ArtisticColorSelectorDock::slotBgColorChanged(const KisColor& color) { m_resourceProvider->resourceManager()->setBackgroundColor( KoColor(color.toKoColor(), m_resourceProvider->resourceManager()->backgroundColor().colorSpace()) ); } void ArtisticColorSelectorDock::slotColorSpaceSelected() { KisColor::Type type = static_cast( m_hsxButtons->id(m_hsxButtons->checkedButton())); m_selectorUI->colorSelector->setColorSpace(type); if (type == KisColor::HSY) { m_preferencesUI->lumaCoefficientBox->show(); } else { m_preferencesUI->lumaCoefficientBox->hide(); } m_selectorUI->colorSelector->setLumaCoefficients( m_preferencesUI->spLumaR->value(), m_preferencesUI->spLumaG->value(), m_preferencesUI->spLumaB->value(), m_preferencesUI->spLumaGamma->value() ); } void ArtisticColorSelectorDock::slotPreferenceChanged() { int hueSteps = DEFAULT_HUE_STEPS; if (m_wheelPrefsUI->bnInfHueSteps->isChecked()) { m_wheelPrefsUI->numHueSteps->setEnabled(false); hueSteps = 1; } else { m_wheelPrefsUI->numHueSteps->setEnabled(true); hueSteps = m_wheelPrefsUI->numHueSteps->value(); } m_selectorUI->colorSelector->setNumPieces(hueSteps); m_selectorUI->colorSelector->setNumRings(m_wheelPrefsUI->numSaturationSteps->value()); int valueScaleSteps; if (m_wheelPrefsUI->bnInfValueScaleSteps->isChecked()) { m_wheelPrefsUI->numValueScaleSteps->setEnabled(false); valueScaleSteps = 1; } else { valueScaleSteps = m_wheelPrefsUI->numValueScaleSteps->value(); m_wheelPrefsUI->numValueScaleSteps->setEnabled(true); } m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps); int defHueSteps; if (m_preferencesUI->bnDefInfHueSteps->isChecked()) { m_preferencesUI->defaultHueSteps->setEnabled(false); defHueSteps = 1; } else { m_preferencesUI->defaultHueSteps->setEnabled(true); defHueSteps = m_preferencesUI->defaultHueSteps->value(); } m_selectorUI->colorSelector->setDefaultHueSteps(defHueSteps); m_selectorUI->colorSelector->setDefaultSaturationSteps(m_preferencesUI->defaultSaturationSteps->value()); int defValueScaleSteps; if (m_preferencesUI->bnDefInfValueScaleSteps->isChecked()) { m_preferencesUI->defaultValueScaleSteps->setEnabled(false); defValueScaleSteps = 1; } else { m_preferencesUI->defaultValueScaleSteps->setEnabled(true); defValueScaleSteps = m_preferencesUI->defaultValueScaleSteps->value(); } m_selectorUI->colorSelector->setDefaultValueScaleSteps(defValueScaleSteps); m_selectorUI->colorSelector->setShowBgColor(m_preferencesUI->showBgColor->isChecked()); m_selectorUI->colorSelector->setShowValueScaleNumbers(m_preferencesUI->showValueScaleNumbers->isChecked()); m_selectorUI->colorSelector->setEnforceGamutMask(m_preferencesUI->enforceGamutMask->isChecked()); m_selectorUI->colorSelector->setInverseSaturation(m_wheelPrefsUI->bnInverseSat->isChecked()); } void ArtisticColorSelectorDock::slotResetDefaultSettings() { quint32 hueSteps = m_selectorUI->colorSelector->getDefaultHueSteps(); quint32 saturationSteps = m_selectorUI->colorSelector->getDefaultSaturationSteps(); quint32 valueScaleSteps = m_selectorUI->colorSelector->getDefaultValueScaleSteps(); m_selectorUI->colorSelector->setNumRings(saturationSteps); m_wheelPrefsUI->numSaturationSteps->blockSignals(true); m_wheelPrefsUI->numSaturationSteps->setValue(saturationSteps); m_wheelPrefsUI->numSaturationSteps->blockSignals(false); m_selectorUI->colorSelector->setNumPieces(hueSteps); m_wheelPrefsUI->numHueSteps->blockSignals(true); m_wheelPrefsUI->numHueSteps->setValue(hueSteps); m_wheelPrefsUI->numHueSteps->blockSignals(false); if (hueSteps == 1) { m_wheelPrefsUI->numHueSteps->setEnabled(false); m_wheelPrefsUI->bnInfHueSteps->setChecked(true); } else { m_wheelPrefsUI->numHueSteps->setEnabled(true); m_wheelPrefsUI->bnInfHueSteps->setChecked(false); } m_selectorUI->colorSelector->setNumLightPieces(valueScaleSteps); m_wheelPrefsUI->numValueScaleSteps->blockSignals(true); m_wheelPrefsUI->numValueScaleSteps->setValue(valueScaleSteps); m_wheelPrefsUI->numValueScaleSteps->blockSignals(false); if (valueScaleSteps == 1) { m_wheelPrefsUI->numValueScaleSteps->setEnabled(false); m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(true); } else { m_wheelPrefsUI->numValueScaleSteps->setEnabled(true); m_wheelPrefsUI->bnInfValueScaleSteps->setChecked(false); } } void ArtisticColorSelectorDock::slotGamutMaskToggle(bool checked) { bool b = (!m_selectedMask) ? false : checked; if (b == true) { m_selectorUI->colorSelector->setGamutMask(m_selectedMask); } m_selectorUI->colorSelector->setGamutMaskOn(b); } void ArtisticColorSelectorDock::setCanvas(KoCanvasBase *canvas) { if (!canvas) { return; } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); } if (m_canvas) { connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), SLOT(slotCanvasResourceChanged(int,QVariant)), Qt::UniqueConnection); connect(m_canvas->displayColorConverter(), SIGNAL(displayConfigurationChanged()), SLOT(slotSelectorSettingsChanged()), Qt::UniqueConnection); m_selectorUI->colorSelector->setColorConverter(m_canvas->displayColorConverter()); setEnabled(true); } } void ArtisticColorSelectorDock::unsetCanvas() { setEnabled(false); m_canvas = nullptr; m_selectorUI->colorSelector->setColorConverter(KisDisplayColorConverter::dumbConverterInstance()); } void ArtisticColorSelectorDock::slotGamutMaskSet(KoGamutMaskSP mask) { if (!mask) { return; } m_selectedMask = mask; if (m_selectedMask) { m_selectorUI->colorSelector->setGamutMask(m_selectedMask); slotGamutMaskToggle(true); } else { slotGamutMaskToggle(false); } } void ArtisticColorSelectorDock::slotGamutMaskUnset() { if (!m_selectedMask) { return; } m_selectedMask = nullptr; slotGamutMaskToggle(false); m_selectorUI->colorSelector->setGamutMask(m_selectedMask); } void ArtisticColorSelectorDock::slotGamutMaskPreviewUpdate() { m_selectorUI->colorSelector->setDirty(); } void ArtisticColorSelectorDock::slotSelectorSettingsChanged() { m_selectorUI->colorSelector->setDirty(); } diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h index 613c6b0167..da336f01b2 100644 --- a/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h +++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_dock.h @@ -1,85 +1,86 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef H_ARTISTIC_COLOR_SELECTOR_DOCK_H #define H_ARTISTIC_COLOR_SELECTOR_DOCK_H #include #include #include #include #include #include #include #include #include #include #include class KisCanvasResourceProvider; class KisColor; class QButtonGroup; class QMenu; struct ArtisticColorSelectorUI; struct ARCSSettingsUI; struct WheelPreferencesPopupUI; class ArtisticColorSelectorDock: public QDockWidget, public KisMainwindowObserver { Q_OBJECT public: ArtisticColorSelectorDock(); ~ArtisticColorSelectorDock() override; QString observerName() override { return "ArtisticColorSelectorDock"; } void setViewManager(KisViewManager* kisview) override; void setCanvas(KoCanvasBase* canvas) override; void unsetCanvas() override; private Q_SLOTS: void slotCanvasResourceChanged(int key, const QVariant& value); void slotFgColorChanged(const KisColor& color); void slotBgColorChanged(const KisColor& color); void slotColorSpaceSelected(); void slotPreferenceChanged(); void slotResetDefaultSettings(); void slotGamutMaskToggle(bool value); void slotGamutMaskSet(KoGamutMaskSP mask); void slotGamutMaskUnset(); void slotGamutMaskPreviewUpdate(); void slotSelectorSettingsChanged(); private: KisCanvas2* m_canvas; KisCanvasResourceProvider* m_resourceProvider; QButtonGroup* m_hsxButtons; ArtisticColorSelectorUI* m_selectorUI; ARCSSettingsUI* m_preferencesUI; WheelPreferencesPopupUI* m_wheelPrefsUI; KoGamutMaskSP m_selectedMask; QIcon m_iconMaskOff; QIcon m_iconMaskOn; QPixmap m_infinityPixmap; }; #endif // H_ARTISTIC_COLOR_SELECTOR_DOCK_H diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.cpp b/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.cpp index 7051282cd8..be960ebc89 100644 --- a/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.cpp +++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.cpp @@ -1,57 +1,58 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "artisticcolorselector_plugin.h" #include "artisticcolorselector_dock.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(PaletteDockPluginFactory, "krita_artisticcolorselector.json", registerPlugin();) class ArtisticColorSelectorDockFactory: public KoDockFactoryBase { public: QString id() const override { return QString("ArtisticColorSelector"); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { ArtisticColorSelectorDock* dockWidget = new ArtisticColorSelectorDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } }; ArtisticColorSelectorPlugin::ArtisticColorSelectorPlugin(QObject* parent, const QVariantList &): QObject(parent) { KoDockRegistry::instance()->add(new ArtisticColorSelectorDockFactory()); } #include "artisticcolorselector_plugin.moc" diff --git a/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.h b/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.h index 808acba69c..98e0e2070d 100644 --- a/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.h +++ b/plugins/dockers/artisticcolorselector/artisticcolorselector_plugin.h @@ -1,30 +1,31 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef H_ARTISTIC_COLOR_SELECTOR_PLUGIN_H #define H_ARTISTIC_COLOR_SELECTOR_PLUGIN_H #include #include class ArtisticColorSelectorPlugin: public QObject { public: ArtisticColorSelectorPlugin(QObject *parent, const QVariantList &); }; #endif // H_ARTISTIC_COLOR_SELECTOR_PLUGIN_H diff --git a/plugins/dockers/artisticcolorselector/kis_arcs_constants.h b/plugins/dockers/artisticcolorselector/kis_arcs_constants.h index 4a40cbbfb4..c0421db5ef 100644 --- a/plugins/dockers/artisticcolorselector/kis_arcs_constants.h +++ b/plugins/dockers/artisticcolorselector/kis_arcs_constants.h @@ -1,54 +1,55 @@ /* * Copyright (c) 2018 Anna Medonosova * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_ARCS_CONSTANTS_H #define KIS_ARCS_CONSTANTS_H #include #include static const int MIN_NUM_HUE_PIECES = 1; static const int MIN_NUM_UI_HUE_PIECES = 2; static const int MAX_NUM_HUE_PIECES = 48; static const int MIN_NUM_LIGHT_PIECES = 1; static const int MIN_NUM_UI_LIGHT_PIECES = 2; static const int MAX_NUM_LIGHT_PIECES = 30; static const int MIN_NUM_SATURATION_RINGS = 1; static const int MAX_NUM_SATURATION_RINGS = 20; static const int DEFAULT_HUE_STEPS = 12; static const int DEFAULT_SATURATION_STEPS = 7; static const int DEFAULT_VALUE_SCALE_STEPS = 11; static const qreal DEFAULT_LUMA_R = 0.2126; static const qreal DEFAULT_LUMA_G = 0.7152; static const qreal DEFAULT_LUMA_B = 0.0722; static const qreal DEFAULT_LUMA_GAMMA = 2.2; // color scheme for the selector static const QColor COLOR_MIDDLE_GRAY = QColor(128,128,128,255); static const QColor COLOR_DARK = QColor(50,50,50,255); static const QColor COLOR_LIGHT = QColor(200,200,200,255); static const QColor COLOR_SELECTED_DARK = QColor(30,30,30,255); static const QColor COLOR_SELECTED_LIGHT = QColor(220,220,220,255); static const QColor COLOR_MASK_FILL = COLOR_MIDDLE_GRAY; static const QColor COLOR_MASK_OUTLINE = COLOR_LIGHT; static const QColor COLOR_MASK_CLEAR = COLOR_LIGHT; static const QColor COLOR_NORMAL_OUTLINE = COLOR_MIDDLE_GRAY; #endif // KIS_ARCS_CONSTANTS_H diff --git a/plugins/dockers/channeldocker/channeldocker.cpp b/plugins/dockers/channeldocker/channeldocker.cpp index 0b8b0239b0..cb24487c99 100644 --- a/plugins/dockers/channeldocker/channeldocker.cpp +++ b/plugins/dockers/channeldocker/channeldocker.cpp @@ -1,87 +1,88 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "channeldocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "channeldocker_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(ChannelDockerPluginFactory, "krita_channeldocker.json", registerPlugin();) class ChannelDockerDockFactory : public KoDockFactoryBase { public: ChannelDockerDockFactory() { } QString id() const override { return QString( "ChannelDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { ChannelDockerDock * dockWidget = new ChannelDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; ChannelDockerPlugin::ChannelDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new ChannelDockerDockFactory()); } ChannelDockerPlugin::~ChannelDockerPlugin() { } #include "channeldocker.moc" diff --git a/plugins/dockers/channeldocker/channeldocker.h b/plugins/dockers/channeldocker/channeldocker.h index bf374e8302..b68e493228 100644 --- a/plugins/dockers/channeldocker/channeldocker.h +++ b/plugins/dockers/channeldocker/channeldocker.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CHANNELDOCKER_H #define CHANNELDOCKER_H #include #include /** * Docker showing the channels of the current layer */ class ChannelDockerPlugin : public QObject { Q_OBJECT public: ChannelDockerPlugin(QObject *parent, const QVariantList &); ~ChannelDockerPlugin() override; }; #endif diff --git a/plugins/dockers/channeldocker/channeldocker_dock.cpp b/plugins/dockers/channeldocker/channeldocker_dock.cpp index 57f385b1d7..ea692a07ab 100644 --- a/plugins/dockers/channeldocker/channeldocker_dock.cpp +++ b/plugins/dockers/channeldocker/channeldocker_dock.cpp @@ -1,126 +1,127 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "channeldocker_dock.h" #include #include #include #include #include #include "channelmodel.h" #include #include #include #include #include #include #include #include "kis_signal_compressor.h" #include #include ChannelDockerDock::ChannelDockerDock( ) : QDockWidget(i18n("Channels")), m_imageIdleWatcher(new KisIdleWatcher(250, this)), m_canvas(0) { m_channelTable = new QTableView(this); m_model = new ChannelModel(this); m_channelTable->setModel(m_model); m_channelTable->setShowGrid(false); m_channelTable->horizontalHeader()->setStretchLastSection(true); m_channelTable->verticalHeader()->setVisible(false); m_channelTable->horizontalHeader()->setVisible(false); m_channelTable->setSelectionBehavior( QAbstractItemView::SelectRows ); QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(m_channelTable); if (scroller){ connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State))); } setWidget(m_channelTable); connect(m_channelTable,&QTableView::activated, m_model, &ChannelModel::rowActivated); } void ChannelDockerDock::setCanvas(KoCanvasBase * canvas) { if(m_canvas == canvas) return; setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } if (!canvas) { m_canvas = 0; return; } m_canvas = dynamic_cast(canvas); if ( m_canvas && m_canvas->image() ) { m_model->slotSetCanvas(m_canvas); KisPaintDeviceSP dev = m_canvas->image()->projection(); m_imageIdleWatcher->setTrackedImage(m_canvas->image()); connect(m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &ChannelDockerDock::updateChannelTable); connect(dev, SIGNAL(colorSpaceChanged(const KoColorSpace*)), m_model, SLOT(slotColorSpaceChanged(const KoColorSpace*))); connect(dev, SIGNAL(colorSpaceChanged(const KoColorSpace*)), m_canvas, SLOT(channelSelectionChanged())); connect(m_model, SIGNAL(channelFlagsChanged()), m_canvas, SLOT(channelSelectionChanged())); m_imageIdleWatcher->startCountdown(); } } void ChannelDockerDock::unsetCanvas() { setEnabled(false); m_canvas = 0; m_model->unsetCanvas(); } void ChannelDockerDock::showEvent(QShowEvent *event) { Q_UNUSED(event); m_imageIdleWatcher->startCountdown(); } void ChannelDockerDock::startUpdateCanvasProjection() { m_imageIdleWatcher->startCountdown(); } void ChannelDockerDock::updateChannelTable() { if (isVisible() && m_canvas && m_canvas->image()){ m_model->updateData(m_canvas); m_channelTable->resizeRowsToContents(); m_channelTable->resizeColumnsToContents(); } } diff --git a/plugins/dockers/channeldocker/channeldocker_dock.h b/plugins/dockers/channeldocker/channeldocker_dock.h index 57ba3531dd..9a295a33db 100644 --- a/plugins/dockers/channeldocker/channeldocker_dock.h +++ b/plugins/dockers/channeldocker/channeldocker_dock.h @@ -1,60 +1,61 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CHANNELDOCKER_DOCK_H #define CHANNELDOCKER_DOCK_H #include #include #include #include #include class ChannelModel; class QTableView; class KisSignalCompressor; class KisIdleWatcher; class ChannelDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: ChannelDockerDock(); QString observerName() override { return "ChannelDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; void showEvent(QShowEvent *event) override; public Q_SLOTS: void startUpdateCanvasProjection(); void slotScrollerStateChanged(QScroller::State state){KisKineticScroller::updateCursor(this, state);} private Q_SLOTS: void updateChannelTable(void); private: KisIdleWatcher* m_imageIdleWatcher; KisSignalCompressor *m_compressor; QPointer m_canvas; QTableView *m_channelTable; ChannelModel *m_model; }; #endif diff --git a/plugins/dockers/channeldocker/channelmodel.cpp b/plugins/dockers/channeldocker/channelmodel.cpp index 9b0f9b4f0b..23fa4bdcc4 100644 --- a/plugins/dockers/channeldocker/channelmodel.cpp +++ b/plugins/dockers/channeldocker/channelmodel.cpp @@ -1,263 +1,264 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "channelmodel.h" #include #include #include #include #include #include #include #include #include ChannelModel::ChannelModel(QObject* parent): QAbstractTableModel(parent), m_canvas(nullptr), m_oversampleRatio(2), m_channelCount(0) { setThumbnailSizeLimit(QSize(64, 64)); } ChannelModel::~ChannelModel() { } QVariant ChannelModel::data(const QModelIndex& index, int role) const { if (m_canvas && index.isValid()) { KisGroupLayerSP rootLayer = m_canvas->image()->rootLayer(); const KoColorSpace* cs = rootLayer->colorSpace(); QList channels = cs->channels(); int channelIndex = index.row(); switch (role) { case Qt::DisplayRole: { if (index.column() == 2) { return channels.at(channelIndex)->name(); } return QVariant(); } case Qt::DecorationRole: { if (index.column() == 1) { Q_ASSERT(m_thumbnails.count() > index.row()); return QVariant(m_thumbnails.at(index.row())); } return QVariant(); } case Qt::CheckStateRole: { Q_ASSERT(index.row() < rowCount()); Q_ASSERT(index.column() < columnCount()); if (index.column() == 0) { QBitArray flags = rootLayer->channelFlags(); return (flags.isEmpty() || flags.testBit(channelIndex)) ? Qt::Checked : Qt::Unchecked; } return QVariant(); } } } return QVariant(); } QVariant ChannelModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section); Q_UNUSED(orientation); Q_UNUSED(role); return QVariant(); } int ChannelModel::rowCount(const QModelIndex& /*parent*/) const { if (!m_canvas) return 0; return m_channelCount; } int ChannelModel::columnCount(const QModelIndex& /*parent*/) const { if (!m_canvas) return 0; //columns are: checkbox, thumbnail, channel name return 3; } bool ChannelModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (m_canvas && m_canvas->image()) { KisGroupLayerSP rootLayer = m_canvas->image()->rootLayer(); const KoColorSpace* cs = rootLayer->colorSpace(); QList channels = cs->channels(); Q_ASSERT(index.row() <= channels.count()); int channelIndex = index.row(); if (role == Qt::CheckStateRole) { QBitArray flags = rootLayer->channelFlags(); flags = flags.isEmpty() ? cs->channelFlags(true, true) : flags; Q_ASSERT(!flags.isEmpty()); flags.setBit(channelIndex, value.toInt() == Qt::Checked); rootLayer->setChannelFlags(flags); emit channelFlagsChanged(); emit dataChanged(this->index(0, 0), this->index(channels.count(), 0)); return true; } } return false; } //User double clicked on a row (but on channel checkbox) //we select this channel, and deselect all other channels (except alpha, which we don't touch) //this makes it fast to select single color channel void ChannelModel::rowActivated(const QModelIndex &index) { if (m_canvas && m_canvas->image()) { KisGroupLayerWSP rootLayer = m_canvas->image()->rootLayer(); const KoColorSpace* cs = rootLayer->colorSpace(); QList channels = cs->channels(); Q_ASSERT(index.row() <= channels.count()); int channelIndex = index.row(); QBitArray flags = rootLayer->channelFlags(); flags = flags.isEmpty() ? cs->channelFlags(true, true) : flags; Q_ASSERT(!flags.isEmpty()); for (int i = 0; i < channels.count(); ++i) { if (channels[i]->channelType() != KoChannelInfo::ALPHA) { flags.setBit(i, (i == channelIndex)); } } rootLayer->setChannelFlags(flags); emit channelFlagsChanged(); emit dataChanged(this->index(0, 0), this->index(channels.count(), 0)); } } Qt::ItemFlags ChannelModel::flags(const QModelIndex& /*index*/) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; return flags; } void ChannelModel::unsetCanvas() { m_canvas = 0; } void ChannelModel::setThumbnailSizeLimit(QSize size) { m_thumbnailSizeLimit = size; updateData(m_canvas); } void ChannelModel::slotSetCanvas(KisCanvas2 *canvas) { if (m_canvas != canvas) { beginResetModel(); m_canvas = canvas; if (m_canvas && m_canvas->image()) { m_channelCount = m_canvas->image()->colorSpace()->channelCount(); updateThumbnails(); } else { m_channelCount = 0; } endResetModel(); } } void ChannelModel::slotColorSpaceChanged(const KoColorSpace *colorSpace) { Q_UNUSED(colorSpace); beginResetModel(); updateThumbnails(); endResetModel(); } void ChannelModel::updateData(KisCanvas2 *canvas) { beginResetModel(); m_canvas = canvas; m_channelCount = (m_canvas) ? m_canvas->image()->colorSpace()->channelCount() : 0; updateThumbnails(); endResetModel(); } //Create thumbnails from full image. //Assumptions: thumbnail size is small compared to the original image and thumbnail quality //doesn't need to be high, so we use fast but not very accurate algorithm. void ChannelModel::updateThumbnails(void) { if (m_canvas && m_canvas->image()) { KisImageSP canvas_image = m_canvas->image(); const KoColorSpace* cs = canvas_image->colorSpace(); m_channelCount = cs->channelCount(); KisPaintDeviceSP dev = canvas_image->projection(); //make sure thumbnail maintains aspect ratio of the original image QSize thumbnailSize(canvas_image->bounds().size()); thumbnailSize.scale(m_thumbnailSizeLimit, Qt::KeepAspectRatio); KisPaintDeviceSP thumbnailDev = dev->createThumbnailDeviceOversampled(thumbnailSize.width(), thumbnailSize.height(), m_oversampleRatio, canvas_image->bounds()); m_thumbnails.resize(m_channelCount); for (quint32 i = 0; i < (quint32)m_channelCount; ++i) { #if QT_VERSION >= 0x050500 m_thumbnails[i] = QImage(thumbnailSize, QImage::Format_Grayscale8); #else m_thumbnails[i] = QImage(thumbnailSize, QImage::Format_ARGB32_Premultiplied); #endif } KisSequentialConstIterator it(thumbnailDev, QRect(0, 0, thumbnailSize.width(), thumbnailSize.height())); for (int y = 0; y < thumbnailSize.height(); y++) { for (int x = 0; x < thumbnailSize.width(); x++) { it.nextPixel(); const quint8* pixel = it.rawDataConst(); for (int chan = 0; chan < m_channelCount; ++chan) { QImage &img = m_thumbnails[chan]; #if QT_VERSION >= 0x050500 *(img.scanLine(y) + x) = cs->scaleToU8(pixel, chan); #else quint8 v = cs->scaleToU8(pixel, chan); img.setPixel(x, y, qRgb(v, v, v)); #endif } } } } else { m_channelCount = 0; } } #include "moc_channelmodel.cpp" diff --git a/plugins/dockers/channeldocker/channelmodel.h b/plugins/dockers/channeldocker/channelmodel.h index a05b73c068..992ae0a8e4 100644 --- a/plugins/dockers/channeldocker/channelmodel.h +++ b/plugins/dockers/channeldocker/channelmodel.h @@ -1,70 +1,71 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CHANNELMODEL_H #define CHANNELMODEL_H #include #include #include #include #include class KoColorSpace; class ChannelModel : public QAbstractTableModel { Q_OBJECT public: ChannelModel(QObject* parent = 0); ~ChannelModel() override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; void unsetCanvas( void ); //set maximum size of the thumbnail image. This should be set based on screen resolution, etc. void setThumbnailSizeLimit(QSize size); public Q_SLOTS: void slotSetCanvas(KisCanvas2* canvas); void slotColorSpaceChanged(const KoColorSpace *colorSpace); void updateData(KisCanvas2 *canvas); void rowActivated(const QModelIndex &index); Q_SIGNALS: void channelFlagsChanged(); private: void updateThumbnails(); private: QPointer m_canvas; QVector m_thumbnails; QSize m_thumbnailSizeLimit; int m_oversampleRatio; int m_channelCount; }; #endif // CHANNELMODEL_H diff --git a/plugins/dockers/compositiondocker/compositiondocker.cpp b/plugins/dockers/compositiondocker/compositiondocker.cpp index acd1324500..afa79677b6 100644 --- a/plugins/dockers/compositiondocker/compositiondocker.cpp +++ b/plugins/dockers/compositiondocker/compositiondocker.cpp @@ -1,86 +1,87 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "compositiondocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "compositiondocker_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(CompositionDockerPluginFactory, "krita_compositiondocker.json", registerPlugin();) class CompositionDockerDockFactory : public KoDockFactoryBase { public: CompositionDockerDockFactory() { } QString id() const override { return QString( "CompositionDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { CompositionDockerDock * dockWidget = new CompositionDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; CompositionDockerPlugin::CompositionDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new CompositionDockerDockFactory()); } CompositionDockerPlugin::~CompositionDockerPlugin() { } #include "compositiondocker.moc" diff --git a/plugins/dockers/compositiondocker/compositiondocker.h b/plugins/dockers/compositiondocker/compositiondocker.h index d715d38316..247132944d 100644 --- a/plugins/dockers/compositiondocker/compositiondocker.h +++ b/plugins/dockers/compositiondocker/compositiondocker.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2012 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef COMPOSITIONDOCKER_H #define COMPOSITIONDOCKER_H #include #include /** * Docker compositions of the image */ class CompositionDockerPlugin : public QObject { Q_OBJECT public: CompositionDockerPlugin(QObject *parent, const QVariantList &); ~CompositionDockerPlugin() override; }; #endif diff --git a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp index 7b7eaacfa7..bfa840a8b7 100644 --- a/plugins/dockers/compositiondocker/compositiondocker_dock.cpp +++ b/plugins/dockers/compositiondocker/compositiondocker_dock.cpp @@ -1,309 +1,310 @@ /* * Copyright (c) 2012 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "compositiondocker_dock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "compositionmodel.h" CompositionDockerDock::CompositionDockerDock( ) : QDockWidget(i18n("Compositions")), m_canvas(0) { QWidget* widget = new QWidget(this); setupUi(widget); m_model = new CompositionModel(this); compositionView->setModel(m_model); compositionView->installEventFilter(this); deleteButton->setIcon(KisIconUtils::loadIcon("edit-delete")); saveButton->setIcon(KisIconUtils::loadIcon("list-add")); exportButton->setIcon(KisIconUtils::loadIcon("document-export")); deleteButton->setToolTip(i18n("Delete Composition")); saveButton->setToolTip(i18n("New Composition")); exportButton->setToolTip(i18n("Export Composition")); setWidget(widget); connect( compositionView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(activated(QModelIndex)) ); compositionView->setContextMenuPolicy(Qt::CustomContextMenu); connect( compositionView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); connect( deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteClicked())); connect( saveButton, SIGNAL(clicked(bool)), this, SLOT(saveClicked())); connect( exportButton, SIGNAL(clicked(bool)), this, SLOT(exportClicked())); saveNameEdit->setPlaceholderText(i18n("Insert Name")); QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(compositionView); if (scroller) { connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State))); } } CompositionDockerDock::~CompositionDockerDock() { } void CompositionDockerDock::setCanvas(KoCanvasBase * canvas) { if (m_canvas && m_canvas->viewManager()) { Q_FOREACH (KisAction *action, m_actions) { m_canvas->viewManager()->actionManager()->takeAction(action); } } unsetCanvas(); setEnabled(canvas != 0); m_canvas = dynamic_cast(canvas); if (m_canvas && m_canvas->viewManager()) { if (m_actions.isEmpty()) { KisAction *updateAction = m_canvas->viewManager()->actionManager()->createAction("update_composition"); connect(updateAction, SIGNAL(triggered()), this, SLOT(updateComposition())); m_actions.append(updateAction); KisAction *renameAction = m_canvas->viewManager()->actionManager()->createAction("rename_composition"); connect(renameAction, SIGNAL(triggered()), this, SLOT(renameComposition())); m_actions.append(renameAction); } else { Q_FOREACH (KisAction *action, m_actions) { m_canvas->viewManager()->actionManager()->addAction(action->objectName(), action); } } updateModel(); } } void CompositionDockerDock::unsetCanvas() { setEnabled(false); m_canvas = 0; m_model->setCompositions(QList()); } void CompositionDockerDock::activated(const QModelIndex& index) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); composition->apply(); } void CompositionDockerDock::deleteClicked() { QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); m_canvas->viewManager()->image()->removeComposition(composition); updateModel(); } } void CompositionDockerDock::saveClicked() { KisImageWSP image = m_canvas->viewManager()->image(); if (!image) return; // format as 001, 002 ... QString name = saveNameEdit->text(); if (name.isEmpty()) { bool found = false; int i = 1; do { name = QString("%1").arg(i, 3, 10, QChar('0')); found = false; Q_FOREACH (KisLayerCompositionSP composition, m_canvas->viewManager()->image()->compositions()) { if (composition->name() == name) { found = true; break; } } i++; } while(found && i < 1000); } KisLayerCompositionSP composition(new KisLayerComposition(image, name)); composition->store(); image->addComposition(composition); saveNameEdit->clear(); updateModel(); compositionView->setCurrentIndex(m_model->index(image->compositions().count()-1, 0)); image->setModified(); } void CompositionDockerDock::updateModel() { if (m_model && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) { m_model->setCompositions(m_canvas->viewManager()->image()->compositions()); } } void CompositionDockerDock::exportClicked() { if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image()) { QString path; KoFileDialog dialog(0, KoFileDialog::OpenDirectory, "compositiondockerdock"); dialog.setCaption(i18n("Select a Directory")); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); path = dialog.filename(); if (path.isNull()) return; if (!path.endsWith('/')) { path.append('/'); } KisImageWSP image = m_canvas->viewManager()->image(); QString filename = m_canvas->viewManager()->document()->localFilePath(); if (!filename.isEmpty()) { QFileInfo info(filename); path += info.baseName() + '_'; } Q_FOREACH (KisLayerCompositionSP composition, m_canvas->viewManager()->image()->compositions()) { if (!composition->isExportEnabled()) { continue; } composition->apply(); image->refreshGraph(); image->lock(); #if 0 image->rootLayer()->projection()->convertToQImage(0, 0, 0, image->width(), image->height()).save(path + composition->name() + ".png"); #else QRect r = image->bounds(); KisDocument *d = KisPart::instance()->createDocument(); KisImageSP dst = new KisImage(d->createUndoStore(), r.width(), r.height(), image->colorSpace(), composition->name()); dst->setResolution(image->xRes(), image->yRes()); d->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", OPACITY_OPAQUE_U8); KisPainter gc(paintLayer->paintDevice()); gc.bitBlt(QPoint(0, 0), image->rootLayer()->projection(), r); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->refreshGraph(); d->setFileBatchMode(true); const QByteArray outputFormat("image/png"); d->exportDocumentSync(QUrl::fromLocalFile(path + composition->name() + ".png"), outputFormat); delete d; #endif image->unlock(); } } } bool CompositionDockerDock::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) { // new index will be set after the method is called QTimer::singleShot(0, this, SLOT(activateCurrentIndex())); } return false; } else { return QObject::eventFilter(obj, event); } } void CompositionDockerDock::activateCurrentIndex() { QModelIndex index = compositionView->currentIndex(); if (index.isValid()) { activated(index); } } void CompositionDockerDock::customContextMenuRequested(QPoint pos) { if (m_actions.isEmpty()) return; QMenu menu; Q_FOREACH (KisAction *action, m_actions) { menu.addAction(action); } menu.exec(compositionView->mapToGlobal(pos)); } void CompositionDockerDock::updateComposition() { QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); composition->store(); m_canvas->image()->setModified(); } } void CompositionDockerDock::renameComposition() { dbgKrita << "rename"; QModelIndex index = compositionView->currentIndex(); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->image() && index.isValid()) { KisLayerCompositionSP composition = m_model->compositionFromIndex(index); bool ok; QString name = QInputDialog::getText(this, i18n("Rename Composition"), i18n("New Name:"), QLineEdit::Normal, composition->name(), &ok); if (ok && !name.isEmpty()) { composition->setName(name); m_canvas->image()->setModified(); } } } diff --git a/plugins/dockers/compositiondocker/compositiondocker_dock.h b/plugins/dockers/compositiondocker/compositiondocker_dock.h index 853fe0ab99..41bcbe33db 100644 --- a/plugins/dockers/compositiondocker/compositiondocker_dock.h +++ b/plugins/dockers/compositiondocker/compositiondocker_dock.h @@ -1,70 +1,71 @@ /* * Copyright (c) 2012 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef COMPOSITIONDOCKER_DOCK_H #define COMPOSITIONDOCKER_DOCK_H #include #include #include #include #include #include "ui_wdgcompositiondocker.h" #include class CompositionModel; class KisAction; class CompositionDockerDock : public QDockWidget, public KoCanvasObserverBase, public Ui_WdgCompositionDocker { Q_OBJECT public: CompositionDockerDock(); ~CompositionDockerDock() override; QString observerName() override { return "CompositionDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; void updateModel(); protected: bool eventFilter(QObject *obj, QEvent *event) override; public Q_SLOTS: void slotScrollerStateChanged(QScroller::State state){ KisKineticScroller::updateCursor(this, state); } private Q_SLOTS: void activated (const QModelIndex& index); void deleteClicked(); void saveClicked(); void exportClicked(); void activateCurrentIndex(); void customContextMenuRequested(QPoint pos); void updateComposition(); void renameComposition(); private: QPointer m_canvas; CompositionModel *m_model; QVector m_actions; }; #endif diff --git a/plugins/dockers/compositiondocker/compositionmodel.cpp b/plugins/dockers/compositiondocker/compositionmodel.cpp index b8561b4aef..2a9f686a25 100644 --- a/plugins/dockers/compositiondocker/compositionmodel.cpp +++ b/plugins/dockers/compositiondocker/compositionmodel.cpp @@ -1,110 +1,111 @@ /* * Copyright (c) 2012 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "compositionmodel.h" #include #include #include CompositionModel::CompositionModel(QObject* parent): QAbstractTableModel(parent) { } CompositionModel::~CompositionModel() { } QVariant CompositionModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { switch (role) { case Qt::DisplayRole: { return m_compositions.at(index.row())->name(); } case Qt::DecorationRole: { return KisIconUtils::loadIcon("tools-wizard"); } case Qt::CheckStateRole: { return m_compositions.at(index.row())->isExportEnabled() ? Qt::Checked : Qt::Unchecked; } } } return QVariant(); } bool CompositionModel::setData ( const QModelIndex& index, const QVariant& value, int role ) { if (index.isValid()) { if (role == Qt::CheckStateRole) { Q_ASSERT(index.row() < rowCount()); Q_ASSERT(index.column() < columnCount()); if (index.column() == 0) { bool exportEnabled = value.toInt() == Qt::Checked; KisLayerCompositionSP layerComposition = m_compositions.at(index.row()); if (layerComposition) { layerComposition->setExportEnabled(exportEnabled); } } } return true; } return false; } QVariant CompositionModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return i18n("Composition"); } int CompositionModel::rowCount(const QModelIndex& /*parent*/) const { return m_compositions.count(); } int CompositionModel::columnCount(const QModelIndex& /*parent*/) const { return 2; } Qt::ItemFlags CompositionModel::flags(const QModelIndex& /*index*/) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; return flags; } KisLayerCompositionSP CompositionModel::compositionFromIndex(const QModelIndex& index) { if(index.isValid()) { return m_compositions.at(index.row()); } return KisLayerCompositionSP(); } void CompositionModel::setCompositions(QList< KisLayerCompositionSP > compositions) { m_compositions = compositions; beginResetModel(); endResetModel(); } diff --git a/plugins/dockers/compositiondocker/compositionmodel.h b/plugins/dockers/compositiondocker/compositionmodel.h index 637b6f8123..51061efb5c 100644 --- a/plugins/dockers/compositiondocker/compositionmodel.h +++ b/plugins/dockers/compositiondocker/compositionmodel.h @@ -1,49 +1,50 @@ /* * Copyright (c) 2012 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef COMPOSITIONMODEL_H #define COMPOSITIONMODEL_H #include #include #include class CompositionModel : public QAbstractTableModel { Q_OBJECT public: CompositionModel(QObject* parent = 0); ~CompositionModel() override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; bool setData ( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; KisLayerCompositionSP compositionFromIndex(const QModelIndex& index); void setCompositions(QList compositions); // public Q_SLOTS: // void clear(); private: QList m_compositions; }; #endif // TASKSETMODEL_H diff --git a/plugins/dockers/digitalmixer/digitalmixer.cc b/plugins/dockers/digitalmixer/digitalmixer.cc index 743b60ac99..0ba5eac3bc 100644 --- a/plugins/dockers/digitalmixer/digitalmixer.cc +++ b/plugins/dockers/digitalmixer/digitalmixer.cc @@ -1,88 +1,89 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "digitalmixer.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "digitalmixer_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(DigitalMixerPluginFactory, "krita_digitalmixer.json", registerPlugin();) class DigitalMixerDockFactory : public KoDockFactoryBase { public: DigitalMixerDockFactory() { } QString id() const override { return QString( "DigitalMixer" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { DigitalMixerDock * dockWidget = new DigitalMixerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; DigitalMixerPlugin::DigitalMixerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new DigitalMixerDockFactory()); } DigitalMixerPlugin::~DigitalMixerPlugin() { m_view = 0; } #include "digitalmixer.moc" diff --git a/plugins/dockers/digitalmixer/digitalmixer.h b/plugins/dockers/digitalmixer/digitalmixer.h index c5b0510c40..8e0dbe726c 100644 --- a/plugins/dockers/digitalmixer/digitalmixer.h +++ b/plugins/dockers/digitalmixer/digitalmixer.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _DIGITALMIXER_H_ #define _DIGITALMIXER_H_ #include #include class KisViewManager; /** * Template of view plugin */ class DigitalMixerPlugin : public QObject { Q_OBJECT public: DigitalMixerPlugin(QObject *parent, const QVariantList &); ~DigitalMixerPlugin() override; private: KisViewManager* m_view; }; #endif diff --git a/plugins/dockers/digitalmixer/digitalmixer_dock.cc b/plugins/dockers/digitalmixer/digitalmixer_dock.cc index eb8b955984..19f8c25b5e 100644 --- a/plugins/dockers/digitalmixer/digitalmixer_dock.cc +++ b/plugins/dockers/digitalmixer/digitalmixer_dock.cc @@ -1,167 +1,168 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "digitalmixer_dock.h" #include #include #include #include #include #include #include #include #include #include #include class DigitalMixerPatch : public KoColorPatch { public: DigitalMixerPatch(QWidget* parent) : KoColorPatch(parent) {} QSize sizeHint() const override { return QSize(24,24); } }; DigitalMixerDock::DigitalMixerDock( ) : QDockWidget(i18n("Digital Colors Mixer")), m_canvas(0) , m_tellCanvas(true) { const KoColorSpace *sRGB = KoColorSpaceRegistry::instance()->rgb8(); KoColor initColors[6] = { KoColor(Qt::black, sRGB), KoColor(Qt::white, sRGB), KoColor(Qt::red, sRGB), KoColor(Qt::green, sRGB), KoColor(Qt::blue, sRGB), KoColor(Qt::yellow, sRGB) }; QWidget* widget = new QWidget(this); QGridLayout* layout = new QGridLayout( widget ); // Current Color m_currentColorPatch = new KoColorPatch(this); m_currentColorPatch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_currentColorPatch->setMinimumWidth(48); layout->addWidget(m_currentColorPatch, 0, 0,3,1); // Create the sliders QSignalMapper* signalMapperSelectColor = new QSignalMapper(this); connect(signalMapperSelectColor, SIGNAL(mapped(int)), SLOT(popupColorChanged(int))); QSignalMapper* signalMapperColorSlider = new QSignalMapper(this); connect(signalMapperColorSlider, SIGNAL(mapped(int)), SLOT(colorSliderChanged(int))); QSignalMapper* signalMapperTargetColor = new QSignalMapper(this); connect(signalMapperTargetColor, SIGNAL(mapped(int)), SLOT(targetColorChanged(int))); for(int i = 0; i < 6; ++i) { Mixer mixer; mixer.targetColor = new DigitalMixerPatch(this); mixer.targetColor->setFixedSize(32, 22); layout->addWidget(mixer.targetColor, 0, i + 1); mixer.targetSlider = new KoColorSlider(Qt::Vertical, this); mixer.targetSlider->setFixedWidth(22); mixer.targetSlider->setMinimumHeight(66); layout->addWidget(mixer.targetSlider, 1, i + 1); mixer.actionColor = new KisColorButton( this ); mixer.actionColor->setColor(initColors[i]); mixer.actionColor->setFixedWidth(22); layout->addWidget(mixer.actionColor, 2, i + 1); m_mixers.push_back(mixer); connect(mixer.actionColor, SIGNAL(changed(KoColor)), signalMapperSelectColor, SLOT(map())); signalMapperSelectColor->setMapping(mixer.actionColor, i); connect(mixer.targetSlider, SIGNAL(valueChanged(int)), signalMapperColorSlider, SLOT(map())); signalMapperColorSlider->setMapping(mixer.targetSlider, i); mixer.targetSlider->setValue(125); connect(mixer.targetColor, SIGNAL(triggered(KoColorPatch*)), signalMapperTargetColor, SLOT(map())); signalMapperTargetColor->setMapping(mixer.targetColor, i); } setCurrentColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); setWidget( widget ); } void DigitalMixerDock::setCanvas(KoCanvasBase * canvas) { setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); } m_canvas = canvas; if (m_canvas) { connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(canvasResourceChanged(int,QVariant))); m_tellCanvas=false; setCurrentColor(m_canvas->resourceManager()->foregroundColor()); m_tellCanvas=true; } } void DigitalMixerDock::popupColorChanged(int i) { KoColor color = m_mixers[i].actionColor->color(); color.convertTo(m_currentColor.colorSpace()); m_mixers[i].targetSlider->setColors( color, m_currentColor); colorSliderChanged(i); } void DigitalMixerDock::colorSliderChanged(int i) { m_mixers[i].targetColor->setColor(m_mixers[i].targetSlider->currentColor()); } void DigitalMixerDock::targetColorChanged(int i) { setCurrentColor(m_mixers[i].targetColor->color()); } void DigitalMixerDock::setCurrentColor(const KoColor& color) { m_currentColor = color; m_currentColorPatch->setColor(color); for(int i = 0; i < m_mixers.size(); ++i) { popupColorChanged(i); colorSliderChanged(i); } if (m_canvas && m_tellCanvas) { m_canvas->resourceManager()->setForegroundColor(m_currentColor); } } void DigitalMixerDock::canvasResourceChanged(int key, const QVariant& v) { m_tellCanvas = false; if (key == KoCanvasResourceProvider::ForegroundColor) setCurrentColor(v.value()); m_tellCanvas = true; } diff --git a/plugins/dockers/digitalmixer/digitalmixer_dock.h b/plugins/dockers/digitalmixer/digitalmixer_dock.h index 251d83fa51..9552f7820b 100644 --- a/plugins/dockers/digitalmixer/digitalmixer_dock.h +++ b/plugins/dockers/digitalmixer/digitalmixer_dock.h @@ -1,63 +1,64 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _DIGITALMIXER_DOCK_H_ #define _DIGITALMIXER_DOCK_H_ #include #include #include #include #include class KoColorPopupAction; class KoColorSlider; class KoColorPatch; class KisColorButton; class DigitalMixerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: DigitalMixerDock( ); QString observerName() override { return "DigitalMixerDock"; } /// reimplemented from KoCanvasObserverBase void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override { m_canvas = 0; setEnabled(false);} public Q_SLOTS: void setCurrentColor(const KoColor& ); void canvasResourceChanged(int, const QVariant&); private Q_SLOTS: void popupColorChanged(int i); void colorSliderChanged(int i); void targetColorChanged(int); private: QPointer m_canvas; KoColor m_currentColor; KoColorPatch* m_currentColorPatch; struct Mixer { KoColorPatch* targetColor; KoColorSlider* targetSlider; KisColorButton* actionColor; }; QList m_mixers; bool m_tellCanvas; }; #endif diff --git a/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp index 4408df062d..507966abfe 100644 --- a/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp +++ b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp @@ -1,247 +1,248 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisGamutMaskChooser.h" #include #include #include #include #include #include #include #include #include #include #include #include /// The resource item delegate for rendering the resource preview class KisGamutMaskDelegate: public QAbstractItemDelegate { public: KisGamutMaskDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent) , m_mode(KisGamutMaskChooser::ViewMode::THUMBNAIL) {} ~KisGamutMaskDelegate() override {} /// reimplemented void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; /// reimplemented QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override { return option.decorationSize; } void setViewMode(KisGamutMaskChooser::ViewMode mode) { m_mode = mode; } private: KisGamutMaskChooser::ViewMode m_mode; }; void KisGamutMaskDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { painter->save(); painter->setRenderHint(QPainter::SmoothPixmapTransform, true); if (!index.isValid()) return; KoResourceSP resource = KoResourceSP(static_cast(index.internalPointer())); KoGamutMaskSP mask = resource.staticCast(); if (!mask) { return; } QImage preview = mask->image(); if(preview.isNull()) { return; } QRect paintRect = option.rect.adjusted(1, 1, -1, -1); if (m_mode == KisGamutMaskChooser::ViewMode::THUMBNAIL) { painter->drawImage(paintRect.x(), paintRect.y(), preview.scaled(paintRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); if (option.state & QStyle::State_Selected) { painter->setCompositionMode(QPainter::CompositionMode_Overlay); painter->setOpacity(0.5); painter->fillRect(paintRect, Qt::white); painter->setCompositionMode(QPainter::CompositionMode_SourceOver); painter->setOpacity(1.0); painter->setPen(QPen(option.palette.highlight(), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); QRect selectedBorder = option.rect.adjusted(1, 1, -1, -1); painter->drawRect(selectedBorder); } } else { QSize previewSize(paintRect.height(), paintRect.height()); painter->drawImage(paintRect.x(), paintRect.y(), preview.scaled(previewSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); int leftMargin = 8; int rightMargin = 7; int vertMargin = 4; int descOffset = 7; QFont font = option.font; font.setBold(true); painter->setFont(font); QRectF titleRect(QPointF(previewSize.width() + leftMargin, paintRect.y() + vertMargin), QPointF(paintRect.width() - rightMargin, paintRect.y() + descOffset + painter->fontMetrics().lineSpacing())); painter->drawText(titleRect, Qt::AlignLeft, painter->fontMetrics().elidedText( mask->title(), Qt::ElideRight, titleRect.width() ) ); if (!mask->description().isEmpty() && !mask->description().isNull()) { font.setPointSize(font.pointSize()-1); font.setBold(false); font.setStyle(QFont::StyleItalic); painter->setFont(font); QRectF descRect(QPointF(previewSize.width() + leftMargin, paintRect.y() + descOffset + painter->fontMetrics().lineSpacing()), QPointF(paintRect.right() - rightMargin, paintRect.bottom() - vertMargin)); int numLines = floor(((float)descRect.height() / (float)painter->fontMetrics().lineSpacing())); if (numLines > 0) { int elideWidth = 0; QTextLayout textLayout(mask->description()); textLayout.beginLayout(); for (int i = 0; i < numLines; i++) { QTextLine line = textLayout.createLine(); if (line.isValid()) { line.setLineWidth(descRect.width()); elideWidth += line.naturalTextWidth(); } } QString elidedText = painter->fontMetrics().elidedText(mask->description(), Qt::ElideRight, elideWidth); painter->drawText(descRect, Qt::AlignLeft|Qt::TextWordWrap, elidedText); } } } painter->restore(); } KisGamutMaskChooser::KisGamutMaskChooser(QWidget *parent) : QWidget(parent) { m_delegate = new KisGamutMaskDelegate(this); m_itemChooser = new KisResourceItemChooser(ResourceType::GamutMasks, false, this); m_itemChooser->setItemDelegate(m_delegate); m_itemChooser->showTaggingBar(true); m_itemChooser->showButtons(false); m_itemChooser->setColumnCount(4); m_itemChooser->setSynced(true); QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0,0,0,0); // TODO: menu for view mode change QMenu* menu = new QMenu(this); menu->setStyleSheet("margin: 6px"); menu->addSection(i18n("Display")); QActionGroup *actionGroup = new QActionGroup(this); KisConfig cfg(true); m_mode = KisGamutMaskChooser::ViewMode(cfg.readEntry("GamutMasks.viewMode", KisGamutMaskChooser::THUMBNAIL)); QAction* action = menu->addAction(KisIconUtils::loadIcon("view-preview"), i18n("Thumbnails"), this, SLOT(slotSetModeThumbnail())); action->setCheckable(true); action->setChecked(m_mode == KisGamutMaskChooser::THUMBNAIL); action->setActionGroup(actionGroup); action = menu->addAction(KisIconUtils::loadIcon("view-list-details"), i18n("Details"), this, SLOT(slotSetModeDetail())); action->setCheckable(true); action->setChecked(m_mode == KisGamutMaskChooser::DETAIL); action->setActionGroup(actionGroup); // setting the view mode setViewMode(m_mode); m_itemChooser->setViewModeButtonVisible(true); QToolButton* viewModeButton = m_itemChooser->viewModeButton(); viewModeButton->setMenu(menu); layout->addWidget(m_itemChooser); setLayout(layout); connect(m_itemChooser, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(resourceSelected(KoResourceSP ))); } KisGamutMaskChooser::~KisGamutMaskChooser() { } void KisGamutMaskChooser::setCurrentResource(KoResourceSP resource) { m_itemChooser->setCurrentResource(resource); } void KisGamutMaskChooser::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); updateViewSettings(); } void KisGamutMaskChooser::setViewMode(KisGamutMaskChooser::ViewMode mode) { m_mode = mode; updateViewSettings(); } void KisGamutMaskChooser::updateViewSettings() { KisConfig cfg(false); cfg.writeEntry("GamutMasks.viewMode", qint32(m_mode)); if (m_mode == KisGamutMaskChooser::THUMBNAIL) { m_itemChooser->setSynced(true); m_delegate->setViewMode(m_mode); } else if (m_mode == KisGamutMaskChooser::DETAIL) { m_itemChooser->setSynced(false); m_itemChooser->setColumnCount(1); m_itemChooser->setRowHeight(this->fontMetrics().lineSpacing()*4); m_itemChooser->setColumnWidth(m_itemChooser->width()); m_delegate->setViewMode(m_mode); } } void KisGamutMaskChooser::resourceSelected(KoResourceSP resource) { emit sigGamutMaskSelected(resource.staticCast()); } void KisGamutMaskChooser::slotSetModeThumbnail() { setViewMode(KisGamutMaskChooser::THUMBNAIL); } void KisGamutMaskChooser::slotSetModeDetail() { setViewMode(KisGamutMaskChooser::DETAIL); } diff --git a/plugins/dockers/gamutmask/KisGamutMaskChooser.h b/plugins/dockers/gamutmask/KisGamutMaskChooser.h index 15a3493a8b..680ee29b62 100644 --- a/plugins/dockers/gamutmask/KisGamutMaskChooser.h +++ b/plugins/dockers/gamutmask/KisGamutMaskChooser.h @@ -1,62 +1,64 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + #ifndef KISGAMUTMASKCHOOSER_H #define KISGAMUTMASKCHOOSER_H #include class KisResourceItemChooser; #include #include class KisGamutMaskDelegate; class KisGamutMaskChooser : public QWidget { Q_OBJECT public: explicit KisGamutMaskChooser(QWidget *parent = nullptr); ~KisGamutMaskChooser() override; enum ViewMode { THUMBNAIL, // Shows thumbnails DETAIL // Shows thumbsnails with text next to it }; void setCurrentResource(KoResourceSP resource); protected: void resizeEvent(QResizeEvent* event) override; Q_SIGNALS: void sigGamutMaskSelected(KoGamutMaskSP mask); private Q_SLOTS: void resourceSelected(KoResourceSP resource); void slotSetModeThumbnail(); void slotSetModeDetail(); private: void setViewMode(ViewMode mode); void updateViewSettings(); KisResourceItemChooser* m_itemChooser; KisGamutMaskDelegate* m_delegate; ViewMode m_mode; }; #endif // KISGAMUTMASKCHOOSER_H diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers/gamutmask/gamutmask_dock.cpp index 9e089f6a6a..3b01a241a5 100644 --- a/plugins/dockers/gamutmask/gamutmask_dock.cpp +++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp @@ -1,628 +1,629 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gamutmask_dock.h" #include #include #include #include #include #include "ui_wdgGamutMaskChooser.h" class KisMainWindow; struct GamutMaskChooserUI: public QWidget, public Ui_wdgGamutMaskChooser { GamutMaskChooserUI() { setupUi(this); } }; GamutMaskDock::GamutMaskDock() : QDockWidget(i18n("Gamut Masks")) , m_resourceProvider(0) , m_selfClosingTemplate(false) , m_externalTemplateClose(false) , m_creatingNewMask(false) , m_templatePrevSaved(false) , m_selfSelectingMask(false) , m_selectedMask(nullptr) , m_maskDocument(nullptr) , m_view(nullptr) { m_dockerUI = new GamutMaskChooserUI(); m_dockerUI->bnMaskEditor->setIcon(KisIconUtils::loadIcon("dirty-preset")); m_dockerUI->bnMaskDelete->setIcon(KisIconUtils::loadIcon("deletelayer")); m_dockerUI->bnMaskNew->setIcon(KisIconUtils::loadIcon("list-add")); m_dockerUI->bnMaskDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer")); m_dockerUI->maskPropertiesBox->setVisible(false); m_dockerUI->bnSaveMask->setIcon(KisIconUtils::loadIcon("document-save")); m_dockerUI->bnCancelMaskEdit->setIcon(KisIconUtils::loadIcon("dialog-cancel")); m_dockerUI->bnPreviewMask->setIcon(KisIconUtils::loadIcon("visible")); QRegularExpression maskTitleRegex("^[-_\\(\\)\\sA-Za-z0-9]+$"); QRegularExpressionValidator* m_maskTitleValidator = new QRegularExpressionValidator(maskTitleRegex, this); m_dockerUI->maskTitleEdit->setValidator(m_maskTitleValidator); KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); rServer->addObserver(this); // gamut mask connections connect(m_dockerUI->bnSaveMask , SIGNAL(clicked()) , SLOT(slotGamutMaskSave())); connect(m_dockerUI->bnCancelMaskEdit , SIGNAL(clicked()) , SLOT(slotGamutMaskCancelEdit())); connect(m_dockerUI->bnPreviewMask , SIGNAL(clicked()) , SLOT(slotGamutMaskPreview())); connect(m_dockerUI->bnMaskEditor , SIGNAL(clicked()) , SLOT(slotGamutMaskEdit())); connect(m_dockerUI->maskChooser, SIGNAL(sigGamutMaskSelected(KoGamutMaskSP)), SLOT(slotGamutMaskSelected(KoGamutMaskSP))); connect(m_dockerUI->bnMaskNew , SIGNAL(clicked()) , SLOT(slotGamutMaskCreateNew())); connect(m_dockerUI->bnMaskDelete , SIGNAL(clicked()) , SLOT(slotGamutMaskDelete())); connect(m_dockerUI->bnMaskDuplicate , SIGNAL(clicked()) , SLOT(slotGamutMaskDuplicate())); setWidget(m_dockerUI); } GamutMaskDock::~GamutMaskDock() { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); rServer->removeObserver(this); } void GamutMaskDock::setViewManager(KisViewManager* kisview) { m_resourceProvider = kisview->canvasResourceProvider(); selectMask(m_resourceProvider->currentGamutMask()); connect(this, SIGNAL(sigGamutMaskSet(KoGamutMaskSP)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), Qt::UniqueConnection); connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), Qt::UniqueConnection); connect(this, SIGNAL(sigGamutMaskUnset()), m_resourceProvider, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection); connect(this, SIGNAL(sigGamutMaskPreviewUpdate()), m_resourceProvider, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection); connect(KisPart::instance(), SIGNAL(sigDocumentRemoved(QString)), this, SLOT(slotDocumentRemoved(QString)), Qt::UniqueConnection); } void GamutMaskDock::slotGamutMaskEdit() { if (!m_selectedMask) { return; } openMaskEditor(); } bool GamutMaskDock::openMaskEditor() { if (!m_selectedMask) { return false; } // find the template resource first, so we can abort the action early on QString maskTemplateFile = KoResourcePaths::findResource(ResourceType::GamutMasks, "GamutMaskTemplate.kra"); if (maskTemplateFile.isEmpty() || maskTemplateFile.isNull() || !QFile::exists(maskTemplateFile)) { dbgPlugins << "GamutMaskDock::openMaskEditor(): maskTemplateFile (" << maskTemplateFile << ") was not found on the system"; getUserFeedback(i18n("Could not open gamut mask for editing."), i18n("The editor template was not found."), QMessageBox::Ok, QMessageBox::Ok, QMessageBox::Critical); return false; } m_dockerUI->maskPropertiesBox->setVisible(true); m_dockerUI->maskPropertiesBox->setEnabled(true); m_dockerUI->editControlsBox->setEnabled(false); m_dockerUI->editControlsBox->setVisible(false); m_dockerUI->maskTitleEdit->setText(m_selectedMask->title()); m_dockerUI->maskDescriptionEdit->setPlainText(m_selectedMask->description()); m_maskDocument = KisPart::instance()->createDocument(); KisPart::instance()->addDocument(m_maskDocument); m_maskDocument->openUrl(QUrl::fromLocalFile(maskTemplateFile), KisDocument::DontAddToRecent); // template document needs a proper autogenerated filename, // to avoid collision with other documents, // otherwise bugs happen when slotDocumentRemoved is called // (e.g. user closes another view, the template stays open, but the edit operation is canceled) m_maskDocument->setInfiniteAutoSaveInterval(); QString maskPath = QString("%1%2%3_%4.kra") .arg(QDir::tempPath()) .arg(QDir::separator()) .arg("GamutMaskTemplate") .arg(std::time(nullptr)); m_maskDocument->setUrl(QUrl::fromLocalFile(maskPath)); m_maskDocument->setLocalFilePath(maskPath); KisShapeLayerSP shapeLayer = getShapeLayer(); // pass only copies of shapes to the layer, // so the originals don't disappear from the mask later for (KoShape *shape: m_selectedMask->koShapes()) { KoShape* newShape = shape->cloneShape(); newShape->setStroke(KoShapeStrokeModelSP()); newShape->setBackground(QSharedPointer(new KoColorBackground(QColor(255,255,255)))); shapeLayer->addShape(newShape); } m_maskDocument->setPreActivatedNode(shapeLayer); // set document as active KisMainWindow* mainWindow = KisPart::instance()->currentMainwindow(); KIS_ASSERT(mainWindow); m_view = mainWindow->addViewAndNotifyLoadingCompleted(m_maskDocument); KIS_ASSERT(m_view); for(KisView *view: KisPart::instance()->views()) { if (view->document() == m_maskDocument) { view->activateWindow(); break; } } connect(m_view->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged())); connect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved())); return true; } void GamutMaskDock::cancelMaskEdit() { if (m_creatingNewMask) { deleteMask(); } if (m_selectedMask) { m_selectedMask->clearPreview(); if (m_resourceProvider->currentGamutMask() == m_selectedMask) { emit sigGamutMaskChanged(m_selectedMask); } } closeMaskDocument(); } void GamutMaskDock::selectMask(KoGamutMaskSP mask, bool notifyItemChooser) { if (!mask) { return; } m_selectedMask = mask; if (notifyItemChooser) { m_selfSelectingMask = true; m_dockerUI->maskChooser->setCurrentResource(m_selectedMask); m_selfSelectingMask = false; } emit sigGamutMaskSet(m_selectedMask); } bool GamutMaskDock::saveSelectedMaskResource() { if (!m_selectedMask || !m_maskDocument) { return false; } bool maskSaved = false; if (m_selectedMask) { QList shapes = getShapesFromLayer(); if (shapes.count() > 0) { m_selectedMask->setMaskShapes(shapes); m_selectedMask->setImage( m_maskDocument->image()->convertToQImage(m_maskDocument->image()->bounds() , m_maskDocument->image()->profile() ) ); m_selectedMask->setDescription(m_dockerUI->maskDescriptionEdit->toPlainText()); m_selectedMask->clearPreview(); m_selectedMask->save(); maskSaved = true; } else { getUserFeedback(i18n("Saving of gamut mask '%1' was aborted.", m_selectedMask->title()), i18n("

The mask template is invalid.

" "

Please check that:" "

    " "
  • your template contains a vector layer named 'maskShapesLayer'
  • " "
  • there are one or more vector shapes on the 'maskShapesLayer'
  • " "

" ), QMessageBox::Ok, QMessageBox::Ok); } } return maskSaved; } void GamutMaskDock::deleteMask() { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); rServer->removeResourceFromServer(m_selectedMask); m_selectedMask = nullptr; } int GamutMaskDock::getUserFeedback(QString text, QString informativeText, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMessageBox::Icon severity) { QMessageBox msgBox; msgBox.setWindowTitle(i18nc("@title:window", "Krita")); msgBox.setText(QString("

%1

").arg(text)); msgBox.setInformativeText(informativeText); msgBox.setStandardButtons(buttons); msgBox.setDefaultButton(defaultButton); msgBox.setIcon(severity); int res = msgBox.exec(); return res; } int GamutMaskDock::saveOrCancel(QMessageBox::StandardButton defaultAction) { int response = 0; if (m_maskDocument->isModified()) { response = getUserFeedback(i18n("Gamut mask '%1' has been modified.", m_selectedMask->title()), i18n("Do you want to save it?"), QMessageBox::Cancel | QMessageBox::Close | QMessageBox::Save, defaultAction); } else if (m_templatePrevSaved && defaultAction != QMessageBox::Close) { response = QMessageBox::Save; } else if (!m_templatePrevSaved) { response = QMessageBox::Close; } else { response = defaultAction; } switch (response) { case QMessageBox::Save : { slotGamutMaskSave(); break; } case QMessageBox::Close : { cancelMaskEdit(); break; } } return response; } KoGamutMaskSP GamutMaskDock::createMaskResource(KoGamutMaskSP sourceMask, QString newTitle) { m_creatingNewMask = true; KoGamutMaskSP newMask; if (sourceMask) { newMask = KoGamutMaskSP(new KoGamutMask(sourceMask.data())); newMask->setImage(sourceMask->image()); } else { newMask = KoGamutMaskSP(new KoGamutMask()); QString defaultPreviewPath = KoResourcePaths::findResource(ResourceType::GamutMasks, "empty_mask_preview.png"); KIS_SAFE_ASSERT_RECOVER_NOOP(!(defaultPreviewPath.isEmpty() || defaultPreviewPath.isNull() || !QFile::exists(defaultPreviewPath))); newMask->setImage(QImage(defaultPreviewPath, "PNG")); } QPair maskFile = resolveMaskTitle(newTitle); QString maskTitle = maskFile.first; QFileInfo fileInfo = maskFile.second; newMask->setTitle(maskTitle); newMask->setFilename(fileInfo.filePath()); newMask->setValid(true); KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); //rServer->removeFromBlacklist(newMask); rServer->addResource(newMask, false); return newMask; } QPair GamutMaskDock::resolveMaskTitle(QString suggestedTitle) { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); QString saveLocation = rServer->saveLocation(); QString processedTitle = suggestedTitle.trimmed(); QString resourceName = processedTitle; while (rServer->resourceByName(resourceName)) { resourceName = resourceName + QString(" (Copy)"); } QString maskTitle = resourceName; QString maskFile = maskTitle + ".kgm"; QString path = saveLocation + maskFile.replace(QRegularExpression("\\s+"), "_"); QFileInfo fileInfo(path); return QPair(maskTitle, fileInfo); } void GamutMaskDock::closeMaskDocument() { if (!m_externalTemplateClose) { if (m_maskDocument) { // set the document to not modified to bypass confirmation dialog // the close is already confirmed m_maskDocument->setModified(false); m_maskDocument->closeUrl(); m_view->closeView(); m_view->deleteLater(); // set a flag that we are doing it ourselves, so the docker does not react to // removing signal from KisPart m_selfClosingTemplate = true; KisPart::instance()->removeView(m_view); KisPart::instance()->removeDocument(m_maskDocument); m_selfClosingTemplate = false; } } m_dockerUI->maskPropertiesBox->setVisible(false); m_dockerUI->editControlsBox->setVisible(true); m_dockerUI->editControlsBox->setEnabled(true); disconnect(m_view->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged())); disconnect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved())); // the template file is meant as temporary, if the user saved it, delete now if (QFile::exists(m_maskDocument->localFilePath())) { QFile::remove(m_maskDocument->localFilePath()); } m_maskDocument = nullptr; m_view = nullptr; m_creatingNewMask = false; m_templatePrevSaved = false; } QList GamutMaskDock::getShapesFromLayer() { KisShapeLayerSP shapeLayer = getShapeLayer(); // create a deep copy of the shapes to save in the mask, // otherwise they vanish when the template closes QList newShapes; if (shapeLayer) { for (KoShape* sh: shapeLayer->shapes()) { KoShape* newShape = sh->cloneShape(); KoShapeStrokeSP border(new KoShapeStroke(0.5f, Qt::white)); newShape->setStroke(border); newShape->setBackground(QSharedPointer(new KoColorBackground(QColor(255,255,255,0)))); newShapes.append(newShape); } } return newShapes; } KisShapeLayerSP GamutMaskDock::getShapeLayer() { KisNodeSP node = m_maskDocument->image()->rootLayer()->findChildByName("maskShapesLayer"); return KisShapeLayerSP(dynamic_cast(node.data())); } void GamutMaskDock::slotGamutMaskSave() { if (!m_selectedMask || !m_maskDocument) { return; } QString newTitle = m_dockerUI->maskTitleEdit->text(); if (m_selectedMask->title() != newTitle) { // title has changed, rename KoGamutMaskSP newMask = createMaskResource(m_selectedMask, newTitle); // delete old mask and select new deleteMask(); selectMask(newMask); } bool maskSaved = saveSelectedMaskResource(); if (maskSaved) { emit sigGamutMaskSet(m_selectedMask); closeMaskDocument(); } } void GamutMaskDock::slotGamutMaskCancelEdit() { if (!m_selectedMask) { return; } saveOrCancel(QMessageBox::Close); } void GamutMaskDock::slotGamutMaskPreview() { if (!m_selectedMask) { return; } m_selectedMask->setPreviewMaskShapes(getShapesFromLayer()); emit sigGamutMaskPreviewUpdate(); } void GamutMaskDock::slotGamutMaskSelected(KoGamutMaskSP mask) { if (!m_selfSelectingMask) { if (m_maskDocument) { int res = saveOrCancel(); if (res == QMessageBox::Cancel) { return; } } selectMask(mask, false); } } void GamutMaskDock::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); } void GamutMaskDock::unsetCanvas() { setEnabled(false); } void GamutMaskDock::unsetResourceServer() { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); rServer->removeObserver(this); } void GamutMaskDock::removingResource(KoGamutMaskSP resource) { // if deleting previously set mask, notify selectors to unset their mask if (resource == m_resourceProvider->currentGamutMask()) { emit sigGamutMaskUnset(); m_selectedMask = nullptr; } } void GamutMaskDock::resourceChanged(KoGamutMaskSP resource) { // if currently set mask has been changed, notify selectors if (resource == m_resourceProvider->currentGamutMask()) { selectMask(resource); } } void GamutMaskDock::slotGamutMaskCreateNew() { KoGamutMaskSP newMask = createMaskResource(nullptr, "new mask"); selectMask(newMask); bool editorOpened = openMaskEditor(); if (!editorOpened) { deleteMask(); } } void GamutMaskDock::slotGamutMaskDuplicate() { if (!m_selectedMask) { return; } KoGamutMaskSP newMask = createMaskResource(m_selectedMask, m_selectedMask->title()); selectMask(newMask); bool editorOpened = openMaskEditor(); if (!editorOpened) { deleteMask(); } } void GamutMaskDock::slotGamutMaskDelete() { if (!m_selectedMask) { return; } int res = getUserFeedback(i18n("Are you sure you want to delete mask '%1'?" , m_selectedMask->title())); if (res == QMessageBox::Yes) { deleteMask(); } } void GamutMaskDock::slotDocumentRemoved(QString filename) { if (!m_maskDocument) { return; } m_externalTemplateClose = true; // we do not want to run this if it is we who close the file if (!m_selfClosingTemplate) { // KisPart called, that a document will be removed // if it's ours, cancel the mask edit operation if (m_maskDocument->url().toLocalFile() == filename) { m_maskDocument->waitForSavingToComplete(); saveOrCancel(); } } m_externalTemplateClose = false; } void GamutMaskDock::slotViewChanged() { if (!m_maskDocument || !m_view) { return; } if (m_view->viewManager()->document() == m_maskDocument) { m_dockerUI->maskPropertiesBox->setEnabled(true); } else { m_dockerUI->maskPropertiesBox->setEnabled(false); } } void GamutMaskDock::slotDocumentSaved() { m_templatePrevSaved = true; } diff --git a/plugins/dockers/gamutmask/gamutmask_dock.h b/plugins/dockers/gamutmask/gamutmask_dock.h index acc11373f9..a625d01dd6 100644 --- a/plugins/dockers/gamutmask/gamutmask_dock.h +++ b/plugins/dockers/gamutmask/gamutmask_dock.h @@ -1,124 +1,125 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef H_GAMUT_MASK_DOCK_H #define H_GAMUT_MASK_DOCK_H #include #include #include #include #include #include #include #include #include #include #include #include #include class KisCanvasResourceProvider; class QButtonGroup; class QMenu; struct GamutMaskChooserUI; class GamutMaskDock: public QDockWidget, public KisMainwindowObserver, public KoResourceServerObserver { Q_OBJECT public: GamutMaskDock(); ~GamutMaskDock() override; QString observerName() override { return "GamutMaskDock"; } void setViewManager(KisViewManager* kisview) override; void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; public: // KoResourceServerObserver void unsetResourceServer() override; void resourceAdded(KoGamutMaskSP /*resource*/) override {} void removingResource(KoGamutMaskSP resource) override; void resourceChanged(KoGamutMaskSP resource) override; void syncTaggedResourceView() override {} void syncTagAddition(const QString&) override {} void syncTagRemoval(const QString&) override {} Q_SIGNALS: void sigGamutMaskSet(KoGamutMaskSP mask); void sigGamutMaskChanged(KoGamutMaskSP mask); void sigGamutMaskUnset(); void sigGamutMaskPreviewUpdate(); private Q_SLOTS: void slotGamutMaskEdit(); void slotGamutMaskSave(); void slotGamutMaskCancelEdit(); void slotGamutMaskSelected(KoGamutMaskSP mask); void slotGamutMaskPreview(); void slotGamutMaskCreateNew(); void slotGamutMaskDuplicate(); void slotGamutMaskDelete(); void slotDocumentRemoved(QString filename); void slotViewChanged(); void slotDocumentSaved(); private: void closeMaskDocument(); bool openMaskEditor(); void cancelMaskEdit(); void selectMask(KoGamutMaskSP mask, bool notifyItemChooser = true); bool saveSelectedMaskResource(); void deleteMask(); int getUserFeedback(QString text, QString informativeText = "", QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton defaultButton = QMessageBox::Yes, QMessageBox::Icon severity = QMessageBox::Warning); int saveOrCancel(QMessageBox::StandardButton defaultAction = QMessageBox::Save); KoGamutMaskSP createMaskResource(KoGamutMaskSP sourceMask, QString newTitle); QPair resolveMaskTitle(QString suggestedTitle); QList getShapesFromLayer(); KisShapeLayerSP getShapeLayer(); KisCanvasResourceProvider* m_resourceProvider; bool m_selfClosingTemplate; bool m_externalTemplateClose; bool m_creatingNewMask; bool m_templatePrevSaved; bool m_selfSelectingMask; GamutMaskChooserUI* m_dockerUI; KisResourceItemChooser* m_maskChooser; KoGamutMaskSP m_selectedMask; QRegExpValidator* m_maskTitleValidator; KisDocument* m_maskDocument; KisView* m_view; }; #endif // H_GAMUT_MASK_DOCK_H diff --git a/plugins/dockers/gamutmask/gamutmask_plugin.cpp b/plugins/dockers/gamutmask/gamutmask_plugin.cpp index 5c94818b63..cd5ca10d09 100644 --- a/plugins/dockers/gamutmask/gamutmask_plugin.cpp +++ b/plugins/dockers/gamutmask/gamutmask_plugin.cpp @@ -1,57 +1,58 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gamutmask_plugin.h" #include "gamutmask_dock.h" #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(PaletteDockPluginFactory, "krita_gamutmask.json", registerPlugin();) class GamutMaskDockFactory: public KoDockFactoryBase { public: QString id() const override { return QString("GamutMask"); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { GamutMaskDock* dockWidget = new GamutMaskDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } }; GamutMaskPlugin::GamutMaskPlugin(QObject* parent, const QVariantList &): QObject(parent) { KoDockRegistry::instance()->add(new GamutMaskDockFactory()); } #include "gamutmask_plugin.moc" diff --git a/plugins/dockers/gamutmask/gamutmask_plugin.h b/plugins/dockers/gamutmask/gamutmask_plugin.h index 005a3fdd25..e26ca0ea9e 100644 --- a/plugins/dockers/gamutmask/gamutmask_plugin.h +++ b/plugins/dockers/gamutmask/gamutmask_plugin.h @@ -1,30 +1,31 @@ /* * Copyright (c) 2018 Anna Medonosova * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef H_GAMUT_MASK_PLUGIN_H #define H_GAMUT_MASK_PLUGIN_H #include #include class GamutMaskPlugin: public QObject { public: GamutMaskPlugin(QObject *parent, const QVariantList &); }; #endif // H_GAMUT_MASK_PLUGIN_H diff --git a/plugins/dockers/griddocker/grid_config_widget.cpp b/plugins/dockers/griddocker/grid_config_widget.cpp index f96b1400fa..198cd0e385 100644 --- a/plugins/dockers/griddocker/grid_config_widget.cpp +++ b/plugins/dockers/griddocker/grid_config_widget.cpp @@ -1,358 +1,359 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "grid_config_widget.h" #include "ui_grid_config_widget.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_debug.h" #include "kis_aspect_ratio_locker.h" #include "kis_int_parse_spin_box.h" #include #include #include #include struct GridConfigWidget::Private { Private() : guiSignalsBlocked(false) {} KisGridConfig gridConfig; KisGuidesConfig guidesConfig; bool guiSignalsBlocked {false}; }; GridConfigWidget::GridConfigWidget(QWidget *parent) : QWidget(parent), ui(new Ui::GridConfigWidget), m_d(new Private) { ui->setupUi(this); ui->colorMain->setAlphaChannelEnabled(true); ui->colorSubdivision->setAlphaChannelEnabled(true); ui->colorGuides->setAlphaChannelEnabled(true); ui->angleLeftSpinbox->setSuffix(QChar(Qt::Key_degree)); ui->angleRightSpinbox->setSuffix(QChar(Qt::Key_degree)); ui->cellSpacingSpinbox->setSuffix(i18n(" px")); ui->gridTypeCombobox->addItem(i18n("Rectangle")); ui->gridTypeCombobox->addItem(i18n("Isometric")); ui->gridTypeCombobox->setCurrentIndex(0); // set to rectangle by default slotGridTypeChanged(); // update the UI to hide any elements we don't need connect(ui->gridTypeCombobox, SIGNAL(currentIndexChanged(int)), SLOT(slotGridTypeChanged())); setGridConfig(m_d->gridConfig); setGuidesConfig(m_d->guidesConfig); // hide offset UI elements if offset is disabled connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblXOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblYOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intXOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intYOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->offsetAspectButton, SLOT(setVisible(bool))); ui->lblXOffset->setVisible(false); ui->lblYOffset->setVisible(false); ui->intXOffset->setVisible(false); ui->intYOffset->setVisible(false); ui->offsetAspectButton->setVisible(false); connect(ui->chkShowGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->chkSnapToGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->chkShowGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->chkSnapToGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->chkLockGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->intSubdivision, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->angleLeftSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->angleRightSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->cellSpacingSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->selectMainStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->colorMain, SIGNAL(changed(QColor)), SLOT(slotGridGuiChanged())); connect(ui->selectSubdivisionStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->colorSubdivision, SIGNAL(changed(QColor)), SLOT(slotGridGuiChanged())); connect(ui->selectGuidesStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->colorGuides, SIGNAL(changed(QColor)), SLOT(slotGuidesGuiChanged())); ui->chkOffset->setChecked(false); KisAspectRatioLocker *offsetLocker = new KisAspectRatioLocker(this); offsetLocker->connectSpinBoxes(ui->intXOffset, ui->intYOffset, ui->offsetAspectButton); KisAspectRatioLocker *spacingLocker = new KisAspectRatioLocker(this); spacingLocker->connectSpinBoxes(ui->intHSpacing, ui->intVSpacing, ui->spacingAspectButton); connect(offsetLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged())); connect(offsetLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged())); connect(spacingLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged())); connect(spacingLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged())); connect(ui->chkShowRulers,SIGNAL(toggled(bool)),SIGNAL(showRulersChanged(bool))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotPreferencesUpdated())); } GridConfigWidget::~GridConfigWidget() { delete ui; } void GridConfigWidget::setGridConfig(const KisGridConfig &value) { KisGridConfig currentConfig = fetchGuiGridConfig(); if (currentConfig == value) return; setGridConfigImpl(value); } void GridConfigWidget::setGuidesConfig(const KisGuidesConfig &value) { KisGuidesConfig currentConfig = fetchGuiGuidesConfig(); if (currentConfig == value) return; setGuidesConfigImpl(value); } void GridConfigWidget::setGridConfigImpl(const KisGridConfig &value) { m_d->gridConfig = value; m_d->guiSignalsBlocked = true; ui->offsetAspectButton->setKeepAspectRatio(m_d->gridConfig.offsetAspectLocked()); ui->spacingAspectButton->setKeepAspectRatio(m_d->gridConfig.spacingAspectLocked()); ui->chkShowGrid->setChecked(m_d->gridConfig.showGrid()); ui->intHSpacing->setValue(m_d->gridConfig.spacing().x()); ui->intHSpacing->setMaximum(std::numeric_limits::max()); ui->intVSpacing->setValue(m_d->gridConfig.spacing().y()); ui->intVSpacing->setMaximum(std::numeric_limits::max()); ui->intXOffset->setValue(m_d->gridConfig.offset().x()); ui->intYOffset->setValue(m_d->gridConfig.offset().y()); ui->intSubdivision->setValue(m_d->gridConfig.subdivision()); ui->chkSnapToGrid->setChecked(m_d->gridConfig.snapToGrid()); ui->angleLeftSpinbox->setValue(m_d->gridConfig.angleLeft()); ui->angleRightSpinbox->setValue(m_d->gridConfig.angleRight()); ui->cellSpacingSpinbox->setValue(m_d->gridConfig.cellSpacing()); ui->selectMainStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeMain())); ui->selectSubdivisionStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeSubdivision())); ui->gridTypeCombobox->setCurrentIndex(m_d->gridConfig.gridType()); ui->colorMain->setColor(m_d->gridConfig.colorMain()); ui->colorSubdivision->setColor(m_d->gridConfig.colorSubdivision()); m_d->guiSignalsBlocked = false; emit gridValueChanged(); } void GridConfigWidget::setGuidesConfigImpl(const KisGuidesConfig &value) { m_d->guidesConfig = value; m_d->guiSignalsBlocked = true; ui->chkShowGuides->setChecked(m_d->guidesConfig.showGuides()); ui->chkSnapToGuides->setChecked(m_d->guidesConfig.snapToGuides()); ui->chkLockGuides->setChecked(m_d->guidesConfig.lockGuides()); ui->selectGuidesStyle->setCurrentIndex(int(m_d->guidesConfig.guidesLineType())); ui->colorGuides->setColor(m_d->guidesConfig.guidesColor()); m_d->guiSignalsBlocked = false; emit guidesValueChanged(); } KisGridConfig GridConfigWidget::gridConfig() const { return m_d->gridConfig; } KisGuidesConfig GridConfigWidget::guidesConfig() const { return m_d->guidesConfig; } KisGridConfig GridConfigWidget::fetchGuiGridConfig() const { KisGridConfig config; config.setShowGrid(ui->chkShowGrid->isChecked()); config.setSnapToGrid(ui->chkSnapToGrid->isChecked()); QPoint pt; pt.rx() = ui->intHSpacing->value(); pt.ry() = ui->intVSpacing->value(); config.setSpacing(pt); pt.rx() = ui->intXOffset->value(); pt.ry() = ui->intYOffset->value(); config.setOffset(pt); config.setSubdivision(ui->intSubdivision->value()); config.setAngleLeft(ui->angleLeftSpinbox->value()); config.setAngleRight(ui->angleRightSpinbox->value()); config.setCellSpacing(ui->cellSpacingSpinbox->value()); config.setGridType(ui->gridTypeCombobox->currentIndex()); config.setOffsetAspectLocked(ui->offsetAspectButton->keepAspectRatio()); config.setSpacingAspectLocked(ui->spacingAspectButton->keepAspectRatio()); config.setLineTypeMain(KisGridConfig::LineTypeInternal(ui->selectMainStyle->currentIndex())); config.setLineTypeSubdivision(KisGridConfig::LineTypeInternal(ui->selectSubdivisionStyle->currentIndex())); config.setColorMain(ui->colorMain->color()); config.setColorSubdivision(ui->colorSubdivision->color()); return config; } KisGuidesConfig GridConfigWidget::fetchGuiGuidesConfig() const { KisGuidesConfig config = m_d->guidesConfig; config.setShowGuides(ui->chkShowGuides->isChecked()); config.setSnapToGuides(ui->chkSnapToGuides->isChecked()); config.setLockGuides(ui->chkLockGuides->isChecked()); config.setGuidesLineType(KisGuidesConfig::LineTypeInternal(ui->selectGuidesStyle->currentIndex())); config.setGuidesColor(ui->colorGuides->color()); return config; } void GridConfigWidget::slotGridGuiChanged() { if (m_d->guiSignalsBlocked) return; KisGridConfig currentConfig = fetchGuiGridConfig(); if (currentConfig == m_d->gridConfig) return; setGridConfigImpl(currentConfig); } void GridConfigWidget::slotPreferencesUpdated() { KisConfig cfg(true); enableIsometricGrid(cfg.useOpenGL()); // Isometric view needs OpenGL } void GridConfigWidget::slotGuidesGuiChanged() { if (m_d->guiSignalsBlocked) return; KisGuidesConfig currentConfig = fetchGuiGuidesConfig(); if (currentConfig == m_d->guidesConfig) return; setGuidesConfigImpl(currentConfig); } void GridConfigWidget::slotGridTypeChanged() { bool showRectangleControls = ui->gridTypeCombobox->currentIndex() == 0; // specific rectangle UI controls ui->lblXSpacing->setVisible(showRectangleControls); ui->lblYSpacing->setVisible(showRectangleControls); ui->intHSpacing->setVisible(showRectangleControls); ui->intVSpacing->setVisible(showRectangleControls); ui->spacingAspectButton->setVisible(showRectangleControls); ui->lblSubdivision->setVisible(showRectangleControls); ui->intSubdivision->setVisible(showRectangleControls); ui->lblSubdivisionStyle->setVisible(showRectangleControls); ui->selectSubdivisionStyle->setVisible(showRectangleControls); ui->colorSubdivision->setVisible(showRectangleControls); // specific isometric UI controls ui->leftAngleLabel->setVisible(!showRectangleControls); ui->rightAngleLabel->setVisible(!showRectangleControls); ui->angleLeftSpinbox->setVisible(!showRectangleControls); ui->angleRightSpinbox->setVisible(!showRectangleControls); ui->cellSpacingLabel->setVisible(!showRectangleControls); ui->cellSpacingSpinbox->setVisible(!showRectangleControls); // disable snapping for isometric grid type for now // remember if we had snapping enabled if it was on the rectangule mode if (!showRectangleControls) { m_isGridEnabled = ui->chkSnapToGrid->isChecked(); ui->chkSnapToGrid->setEnabled(false); ui->chkSnapToGrid->setChecked(false); } else { ui->chkSnapToGrid->setEnabled(true); ui->chkSnapToGrid->setChecked(m_isGridEnabled); } slotGridGuiChanged(); } bool GridConfigWidget::showRulers() const { return ui->chkShowRulers->isChecked(); } void GridConfigWidget::enableIsometricGrid(bool value) { m_isIsometricGridEnabled = value; // Isometric grids disabled if OpenGL is disabled QStandardItemModel *model = qobject_cast(ui->gridTypeCombobox->model()); QStandardItem *item = model->item(1); // isometric option // item->setFlags(m_isIsometricGridEnabled ? item->flags() & ~Qt::ItemIsEnabled: // item->flags() | Qt::ItemIsEnabled); item->setEnabled(m_isIsometricGridEnabled); if (m_isIsometricGridEnabled) { item->setText(i18n("Isometric")); } else { item->setText(i18n("Isometric (requires OpenGL)")); // change drop down index to Rectangular in case it was previously set to isometric ui->gridTypeCombobox->setCurrentIndex(0); } } void GridConfigWidget::setShowRulers(bool value) { ui->chkShowRulers->setChecked(value); } diff --git a/plugins/dockers/griddocker/grid_config_widget.h b/plugins/dockers/griddocker/grid_config_widget.h index 1aa90828c0..e89bad031f 100644 --- a/plugins/dockers/griddocker/grid_config_widget.h +++ b/plugins/dockers/griddocker/grid_config_widget.h @@ -1,81 +1,82 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GRID_CONFIG_WIDGET_H #define GRID_CONFIG_WIDGET_H #include #include namespace Ui { class GridConfigWidget; } class KisGridConfig; class KisGuidesConfig; class GridConfigWidget : public QWidget { Q_OBJECT public: explicit GridConfigWidget(QWidget *parent = 0); ~GridConfigWidget() override; void setGridConfig(const KisGridConfig &value); KisGridConfig gridConfig() const; void setGuidesConfig(const KisGuidesConfig &value); KisGuidesConfig guidesConfig() const; bool showRulers() const; void enableIsometricGrid(bool value); public Q_SLOTS: void setShowRulers(bool value); private Q_SLOTS: void slotGridGuiChanged(); void slotGuidesGuiChanged(); void slotGridTypeChanged(); void slotPreferencesUpdated(); Q_SIGNALS: void gridValueChanged(); void guidesValueChanged(); void showRulersChanged(bool); private: KisGridConfig fetchGuiGridConfig() const; void setGridConfigImpl(const KisGridConfig &value); KisGuidesConfig fetchGuiGuidesConfig() const; void setGuidesConfigImpl(const KisGuidesConfig &value); private: Ui::GridConfigWidget *ui; struct Private; const QScopedPointer m_d; bool m_isGridEnabled {false}; bool m_isIsometricGridEnabled {true}; }; #endif // GRID_CONFIG_WIDGET_H diff --git a/plugins/dockers/griddocker/griddocker.cpp b/plugins/dockers/griddocker/griddocker.cpp index 3dd6a87f7b..d5aeb698c7 100644 --- a/plugins/dockers/griddocker/griddocker.cpp +++ b/plugins/dockers/griddocker/griddocker.cpp @@ -1,87 +1,88 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "griddocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "griddocker_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(GridDockerPluginFactory, "krita_griddocker.json", registerPlugin();) class GridDockerDockFactory : public KoDockFactoryBase { public: GridDockerDockFactory() { } QString id() const override { return QString( "GridDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { GridDockerDock * dockWidget = new GridDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; GridDockerPlugin::GridDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new GridDockerDockFactory()); } GridDockerPlugin::~GridDockerPlugin() { m_view = 0; } #include "griddocker.moc" diff --git a/plugins/dockers/griddocker/griddocker.h b/plugins/dockers/griddocker/griddocker.h index fa7df907cf..9de7f52fe2 100644 --- a/plugins/dockers/griddocker/griddocker.h +++ b/plugins/dockers/griddocker/griddocker.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GRID_DOCKER_H_ #define _GRID_DOCKER_H_ #include #include class KisViewManager; /** * Template of view plugin */ class GridDockerPlugin : public QObject { Q_OBJECT public: GridDockerPlugin(QObject *parent, const QVariantList &); ~GridDockerPlugin() override; private: KisViewManager* m_view; }; #endif diff --git a/plugins/dockers/griddocker/griddocker_dock.cpp b/plugins/dockers/griddocker/griddocker_dock.cpp index 5615e8d23d..0c6a1be2e8 100644 --- a/plugins/dockers/griddocker/griddocker_dock.cpp +++ b/plugins/dockers/griddocker/griddocker_dock.cpp @@ -1,128 +1,129 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "griddocker_dock.h" //#include "gridwidget.h" // #include // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grid_config_widget.h" GridDockerDock::GridDockerDock( ) : QDockWidget(i18n("Grid and Guides")) , m_canvas(0) { m_configWidget = new GridConfigWidget(this); connect(m_configWidget, SIGNAL(gridValueChanged()), SLOT(slotGuiGridConfigChanged())); connect(m_configWidget, SIGNAL(guidesValueChanged()), SLOT(slotGuiGuidesConfigChanged())); setWidget(m_configWidget); setEnabled(m_canvas); } GridDockerDock::~GridDockerDock() { } void GridDockerDock::setCanvas(KoCanvasBase * canvas) { if(canvas && m_canvas == canvas) return; if (m_canvas) { m_canvasConnections.clear(); m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } m_canvas = canvas ? dynamic_cast(canvas) : 0; setEnabled(m_canvas); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->document()) { m_canvasConnections.addConnection( m_canvas->viewManager()->gridManager(), SIGNAL(sigRequestUpdateGridConfig(KisGridConfig)), this, SLOT(slotGridConfigUpdateRequested(KisGridConfig))); slotGridConfigUpdateRequested(m_canvas->viewManager()->document()->gridConfig()); KisAction* action = m_canvas->viewManager()->actionManager()->actionByName("view_ruler"); m_canvasConnections.addConnection(m_configWidget,SIGNAL(showRulersChanged(bool)),action,SLOT(setChecked(bool))); m_canvasConnections.addConnection(action,SIGNAL(toggled(bool)),m_configWidget,SLOT(setShowRulers(bool))); m_configWidget->setShowRulers(action->isChecked()); m_canvasConnections.addConnection( m_canvas->viewManager()->guidesManager(), SIGNAL(sigRequestUpdateGuidesConfig(KisGuidesConfig)), this, SLOT(slotGuidesConfigUpdateRequested(KisGuidesConfig))); slotGuidesConfigUpdateRequested(m_canvas->viewManager()->document()->guidesConfig()); // isometric grid only available with OpenGL if (m_canvas->canvasIsOpenGL()) { m_configWidget->enableIsometricGrid(true); } else { m_configWidget->enableIsometricGrid(false); } } } void GridDockerDock::unsetCanvas() { setCanvas(0); } void GridDockerDock::slotGuiGridConfigChanged() { if (!m_canvas) return; m_canvas->viewManager()->gridManager()->setGridConfig(m_configWidget->gridConfig()); } void GridDockerDock::slotGridConfigUpdateRequested(const KisGridConfig &config) { m_configWidget->setGridConfig(config); } void GridDockerDock::slotGuiGuidesConfigChanged() { if (!m_canvas) return; m_canvas->viewManager()->guidesManager()->setGuidesConfig(m_configWidget->guidesConfig()); } void GridDockerDock::slotGuidesConfigUpdateRequested(const KisGuidesConfig &config) { m_configWidget->setGuidesConfig(config); } diff --git a/plugins/dockers/griddocker/griddocker_dock.h b/plugins/dockers/griddocker/griddocker_dock.h index d7d1a775d8..b156c23eab 100644 --- a/plugins/dockers/griddocker/griddocker_dock.h +++ b/plugins/dockers/griddocker/griddocker_dock.h @@ -1,55 +1,56 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GRID_DOCK_H_ #define _GRID_DOCK_H_ #include #include #include "kis_signal_auto_connection.h" class QVBoxLayout; class KisCanvas2; class GridConfigWidget; class KisSignalAutoConnection; class KisGridConfig; class KisGuidesConfig; class GridDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: GridDockerDock(); ~GridDockerDock() override; QString observerName() override { return "GridDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; public Q_SLOTS: void slotGuiGridConfigChanged(); void slotGridConfigUpdateRequested(const KisGridConfig &config); void slotGuiGuidesConfigChanged(); void slotGuidesConfigUpdateRequested(const KisGuidesConfig &config); private: GridConfigWidget *m_configWidget; QPointer m_canvas; KisSignalAutoConnectionsStore m_canvasConnections; }; #endif diff --git a/plugins/dockers/histogram/histogramdocker.cpp b/plugins/dockers/histogram/histogramdocker.cpp index 623bce5f92..93dd11349a 100644 --- a/plugins/dockers/histogram/histogramdocker.cpp +++ b/plugins/dockers/histogram/histogramdocker.cpp @@ -1,87 +1,88 @@ /* * Copyright (c) 2016 Eugene Ingerman * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "histogramdocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "histogramdocker_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(HistogramDockerPluginFactory, "krita_histogramdocker.json", registerPlugin();) class HistogramDockerDockFactory : public KoDockFactoryBase { public: HistogramDockerDockFactory() { } QString id() const override { return QString( "HistogramDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { HistogramDockerDock * dockWidget = new HistogramDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } private: }; HistogramDockerPlugin::HistogramDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new HistogramDockerDockFactory()); } HistogramDockerPlugin::~HistogramDockerPlugin() { m_view = 0; } #include "histogramdocker.moc" diff --git a/plugins/dockers/histogram/histogramdocker.h b/plugins/dockers/histogram/histogramdocker.h index 0e73af0b5e..4bb0047f43 100644 --- a/plugins/dockers/histogram/histogramdocker.h +++ b/plugins/dockers/histogram/histogramdocker.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2016 Eugene Ingerman * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _HISTOGRAM_DOCKER_H_ #define _HISTOGRAM_DOCKER_H_ #include #include class KisViewManager; /** * Template of view plugin */ class HistogramDockerPlugin : public QObject { Q_OBJECT public: HistogramDockerPlugin(QObject *parent, const QVariantList &); ~HistogramDockerPlugin() override; private: KisViewManager* m_view; }; #endif diff --git a/plugins/dockers/histogram/histogramdocker_dock.cpp b/plugins/dockers/histogram/histogramdocker_dock.cpp index 4028c25c7f..e537851c74 100644 --- a/plugins/dockers/histogram/histogramdocker_dock.cpp +++ b/plugins/dockers/histogram/histogramdocker_dock.cpp @@ -1,111 +1,112 @@ /* * Copyright (c) 2016 Eugene Ingerman * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "histogramdocker_dock.h" #include #include #include #include "kis_canvas2.h" #include #include #include "kis_image.h" #include "kis_paint_device.h" #include "kis_idle_watcher.h" #include "histogramdockerwidget.h" HistogramDockerDock::HistogramDockerDock() : QDockWidget(i18n("Histogram")), m_imageIdleWatcher(new KisIdleWatcher(250, this)), m_canvas(0) { QWidget *page = new QWidget(this); m_layout = new QVBoxLayout(page); m_histogramWidget = new HistogramDockerWidget(this); m_histogramWidget->setBackgroundRole(QPalette::AlternateBase); m_histogramWidget->setAutoFillBackground(true); // paints background role before paint() m_histogramWidget->setMinimumHeight(50); //m_histogramWidget->setSmoothHistogram(false); m_layout->addWidget(m_histogramWidget, 1); setWidget(page); connect(m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &HistogramDockerDock::updateHistogram); } void HistogramDockerDock::setCanvas(KoCanvasBase * canvas) { if (m_canvas == canvas) return; setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_histogramWidget->setPaintDevice(m_canvas); m_imageIdleWatcher->setTrackedImage(m_canvas->image()); connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)), this, SLOT(startUpdateCanvasProjection()), Qt::UniqueConnection); connect(m_canvas->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SLOT(sigColorSpaceChanged(const KoColorSpace*)), Qt::UniqueConnection); m_imageIdleWatcher->startCountdown(); } } void HistogramDockerDock::unsetCanvas() { setEnabled(false); m_canvas = 0; m_histogramWidget->setPaintDevice(m_canvas); m_imageIdleWatcher->startCountdown(); } void HistogramDockerDock::startUpdateCanvasProjection() { if (isVisible()) { m_imageIdleWatcher->startCountdown(); } } void HistogramDockerDock::showEvent(QShowEvent *event) { Q_UNUSED(event); m_imageIdleWatcher->startCountdown(); } void HistogramDockerDock::sigColorSpaceChanged(const KoColorSpace */*cs*/) { if (isVisible()) { m_imageIdleWatcher->startCountdown(); } } void HistogramDockerDock::updateHistogram() { if (isVisible()) { m_histogramWidget->updateHistogram(); } } diff --git a/plugins/dockers/histogram/histogramdocker_dock.h b/plugins/dockers/histogram/histogramdocker_dock.h index 583a95e70c..53c074d26a 100644 --- a/plugins/dockers/histogram/histogramdocker_dock.h +++ b/plugins/dockers/histogram/histogramdocker_dock.h @@ -1,59 +1,60 @@ /* * Copyright (c) 2016 Eugene Ingerman * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _HISTOGRAM_DOCK_H_ #define _HISTOGRAM_DOCK_H_ #include #include #include #include #include class QVBoxLayout; class KisIdleWatcher; class KoHistogramProducer; class HistogramDockerWidget; class HistogramDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: HistogramDockerDock(); QString observerName() override { return "HistogramDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; public Q_SLOTS: void startUpdateCanvasProjection(); void sigColorSpaceChanged(const KoColorSpace* cs); void updateHistogram(); protected: void showEvent(QShowEvent *event) override; private: QVBoxLayout *m_layout; KisIdleWatcher *m_imageIdleWatcher; HistogramDockerWidget *m_histogramWidget; QPointer m_canvas; }; #endif diff --git a/plugins/dockers/histogram/histogramdockerwidget.cpp b/plugins/dockers/histogram/histogramdockerwidget.cpp index 30e51285ab..5c8e29d929 100644 --- a/plugins/dockers/histogram/histogramdockerwidget.cpp +++ b/plugins/dockers/histogram/histogramdockerwidget.cpp @@ -1,198 +1,199 @@ /* * Copyright (c) 2016 Eugene Ingerman * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "histogramdockerwidget.h" #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "kis_paint_device.h" #include "KoColorSpace.h" #include "kis_iterator_ng.h" #include "kis_canvas2.h" HistogramDockerWidget::HistogramDockerWidget(QWidget *parent, const char *name, Qt::WindowFlags f) : QLabel(parent, f), m_paintDevice(nullptr), m_smoothHistogram(true) { setObjectName(name); } HistogramDockerWidget::~HistogramDockerWidget() { } void HistogramDockerWidget::setPaintDevice(KisCanvas2* canvas) { if (canvas) { m_paintDevice = canvas->image()->projection(); m_bounds = canvas->image()->bounds(); } else { m_paintDevice.clear(); m_bounds = QRect(); m_histogramData.clear(); } } void HistogramDockerWidget::updateHistogram() { if (!m_paintDevice.isNull()) { KisPaintDeviceSP m_devClone = new KisPaintDevice(m_paintDevice->colorSpace()); m_devClone->makeCloneFrom(m_paintDevice, m_bounds); HistogramComputationThread *workerThread = new HistogramComputationThread(m_devClone, m_bounds); connect(workerThread, &HistogramComputationThread::resultReady, this, &HistogramDockerWidget::receiveNewHistogram); connect(workerThread, &HistogramComputationThread::finished, workerThread, &QObject::deleteLater); workerThread->start(); } else { m_histogramData.clear(); update(); } } void HistogramDockerWidget::receiveNewHistogram(HistVector *histogramData) { m_histogramData = *histogramData; update(); } void HistogramDockerWidget::paintEvent(QPaintEvent *event) { if (m_paintDevice && !m_histogramData.empty()) { int nBins = m_histogramData.at(0).size(); const KoColorSpace* cs = m_paintDevice->colorSpace(); QLabel::paintEvent(event); QPainter painter(this); painter.fillRect(0, 0, this->width(), this->height(), this->palette().dark().color()); painter.setPen(this->palette().light().color()); const int NGRID = 4; for (int i = 0; i <= NGRID; ++i) { painter.drawLine(this->width()*i / NGRID, 0., this->width()*i / NGRID, this->height()); painter.drawLine(0., this->height()*i / NGRID, this->width(), this->height()*i / NGRID); } unsigned int nChannels = cs->channelCount(); QList channels = cs->channels(); unsigned int highest = 0; //find the most populous bin in the histogram to scale it properly for (int chan = 0; chan < channels.size(); chan++) { if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) { std::vector histogramTemp = m_histogramData.at(chan); //use 98th percentile, rather than max for better visual appearance int nthPercentile = 2 * histogramTemp.size() / 100; //unsigned int max = *std::max_element(m_histogramData.at(chan).begin(),m_histogramData.at(chan).end()); std::nth_element(histogramTemp.begin(), histogramTemp.begin() + nthPercentile, histogramTemp.end(), std::greater()); unsigned int max = *(histogramTemp.begin() + nthPercentile); highest = std::max(max, highest); } } painter.setWindow(QRect(-1, 0, nBins + 1, highest)); painter.setCompositionMode(QPainter::CompositionMode_Plus); for (int chan = 0; chan < (int)nChannels; chan++) { if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) { QColor color = channels.at(chan)->color(); //special handling of grayscale color spaces. can't use color returned above. if(cs->colorChannelCount()==1){ color = QColor(Qt::gray); } QColor fill_color = color; fill_color.setAlphaF(.25); painter.setBrush(fill_color); QPen pen = QPen(color); pen.setWidth(0); painter.setPen(pen); if (m_smoothHistogram) { QPainterPath path; path.moveTo(QPointF(-1, highest)); for (qint32 i = 0; i < nBins; ++i) { float v = std::max((float)highest - m_histogramData[chan][i], 0.f); path.lineTo(QPointF(i, v)); } path.lineTo(QPointF(nBins + 1, highest)); path.closeSubpath(); painter.drawPath(path); } else { pen.setWidth(1); painter.setPen(pen); for (qint32 i = 0; i < nBins; ++i) { float v = std::max((float)highest - m_histogramData[chan][i], 0.f); painter.drawLine(QPointF(i, highest), QPointF(i, v)); } } } } } } void HistogramComputationThread::run() { const KoColorSpace *cs = m_dev->colorSpace(); quint32 channelCount = m_dev->channelCount(); quint32 pixelSize = m_dev->pixelSize(); quint32 imageSize = m_bounds.width() * m_bounds.height(); quint32 nSkip = 1 + (imageSize >> 20); //for speed use about 1M pixels for computing histograms //allocate space for the histogram data bins.resize((int)channelCount); for (auto &bin : bins) { bin.resize(std::numeric_limits::max() + 1); } QRect bounds = m_dev->exactBounds(); if (bounds.isEmpty()) return; quint32 toSkip = nSkip; KisSequentialConstIterator it(m_dev, m_dev->exactBounds()); int numConseqPixels = it.nConseqPixels(); while (it.nextPixels(numConseqPixels)) { numConseqPixels = it.nConseqPixels(); const quint8* pixel = it.rawDataConst(); for (int k = 0; k < numConseqPixels; ++k) { if (--toSkip == 0) { for (int chan = 0; chan < (int)channelCount; ++chan) { bins[chan][cs->scaleToU8(pixel, chan)]++; } toSkip = nSkip; } pixel += pixelSize; } } emit resultReady(&bins); } diff --git a/plugins/dockers/histogram/histogramdockerwidget.h b/plugins/dockers/histogram/histogramdockerwidget.h index a482583c11..156db1fad2 100644 --- a/plugins/dockers/histogram/histogramdockerwidget.h +++ b/plugins/dockers/histogram/histogramdockerwidget.h @@ -1,74 +1,75 @@ /* * Copyright (c) 2016 Eugene Ingerman * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef HISTOGRAMDOCKERWIDGET_H #define HISTOGRAMDOCKERWIDGET_H #include #include #include #include #include "kis_types.h" #include class KisCanvas2; typedef std::vector > HistVector; //Don't use QVector here - it's too slow for this purpose class HistogramComputationThread : public QThread { Q_OBJECT public: HistogramComputationThread(KisPaintDeviceSP _dev, const QRect& _bounds) : m_dev(_dev), m_bounds(_bounds) {} void run() override; Q_SIGNALS: void resultReady(HistVector*); private: KisPaintDeviceSP m_dev; QRect m_bounds; HistVector bins; }; class HistogramDockerWidget : public QLabel { Q_OBJECT public: HistogramDockerWidget(QWidget *parent = 0, const char *name = 0, Qt::WindowFlags f = 0); ~HistogramDockerWidget() override; void setPaintDevice(KisCanvas2* canvas); void paintEvent(QPaintEvent *event) override; public Q_SLOTS: void updateHistogram(); void receiveNewHistogram(HistVector*); private: KisPaintDeviceSP m_paintDevice; HistVector m_histogramData; QRect m_bounds; bool m_smoothHistogram; }; #endif // HISTOGRAMDOCKERWIDGET_H diff --git a/plugins/dockers/layerdocker/LayerDocker.cpp b/plugins/dockers/layerdocker/LayerDocker.cpp index cbef015e08..ba5683c8e3 100644 --- a/plugins/dockers/layerdocker/LayerDocker.cpp +++ b/plugins/dockers/layerdocker/LayerDocker.cpp @@ -1,41 +1,42 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "LayerDocker.h" #include #include #include #include "kis_debug.h" #include "LayerBox.h" K_PLUGIN_FACTORY_WITH_JSON(KritaLayerDockerPluginFactory, "kritalayerdocker.json", registerPlugin();) KritaLayerDockerPlugin::KritaLayerDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new LayerBoxFactory()); } KritaLayerDockerPlugin::~KritaLayerDockerPlugin() { } #include "LayerDocker.moc" diff --git a/plugins/dockers/layerdocker/LayerDocker.h b/plugins/dockers/layerdocker/LayerDocker.h index e7c0729930..be323bd581 100644 --- a/plugins/dockers/layerdocker/LayerDocker.h +++ b/plugins/dockers/layerdocker/LayerDocker.h @@ -1,37 +1,38 @@ /* * Copyright (c) 2009 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _DEFAULT_DOCKERS_H #define _DEFAULT_DOCKERS_H #include #include /** * Template of view plugin */ class KritaLayerDockerPlugin : public QObject { Q_OBJECT public: KritaLayerDockerPlugin(QObject *parent, const QVariantList &); ~KritaLayerDockerPlugin() override; }; #endif diff --git a/plugins/dockers/logdocker/LogDocker.cpp b/plugins/dockers/logdocker/LogDocker.cpp index e65a9e4a3d..8146da5426 100644 --- a/plugins/dockers/logdocker/LogDocker.cpp +++ b/plugins/dockers/logdocker/LogDocker.cpp @@ -1,80 +1,80 @@ /* -* Copyright (c) 2018 Boudewijn Rempt + * Copyright (c) 2018 Boudewijn Rempt * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - #include "LogDocker.h" #include #include #include #include #include "LogDockerDock.h" K_PLUGIN_FACTORY_WITH_JSON(LogDockerPluginFactory, "krita_logdocker.json", registerPlugin();) class LogDockerDockFactory : public KoDockFactoryBase { public: LogDockerDockFactory() { } virtual ~LogDockerDockFactory() { } QString id() const override { return QString( "LogDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { LogDockerDock * dockWidget = new LogDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; LogDockerPlugin::LogDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new LogDockerDockFactory()); } LogDockerPlugin::~LogDockerPlugin() { } #include "LogDocker.moc" diff --git a/plugins/dockers/logdocker/LogDocker.h b/plugins/dockers/logdocker/LogDocker.h index 40ac6a099e..07b4872f77 100644 --- a/plugins/dockers/logdocker/LogDocker.h +++ b/plugins/dockers/logdocker/LogDocker.h @@ -1,32 +1,34 @@ /* * Copyright (c) 2018 Boudewijn Rempt * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + #ifndef _LOG_DOCKER_H_ #define _LOG_DOCKER_H_ #include #include class LogDockerPlugin : public QObject { Q_OBJECT public: LogDockerPlugin(QObject *parent, const QVariantList &); ~LogDockerPlugin() override; }; #endif diff --git a/plugins/dockers/logdocker/LogDockerDock.cpp b/plugins/dockers/logdocker/LogDockerDock.cpp index dedbed5e6a..6271ab7156 100644 --- a/plugins/dockers/logdocker/LogDockerDock.cpp +++ b/plugins/dockers/logdocker/LogDockerDock.cpp @@ -1,336 +1,338 @@ /* * Copyright (c) 2018 Boudewijn Rempt * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + #include "LogDockerDock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas2.h" #include "KisViewManager.h" #include "kis_config.h" MessageSender *LogDockerDock::s_messageSender {new MessageSender()}; QTextCharFormat LogDockerDock::s_debug; QTextCharFormat LogDockerDock::s_info; QTextCharFormat LogDockerDock::s_warning; QTextCharFormat LogDockerDock::s_critical; QTextCharFormat LogDockerDock::s_fatal; LogDockerDock::LogDockerDock( ) : QDockWidget(i18n("Log Viewer")) { QWidget *page = new QWidget(this); setupUi(page); setWidget(page); bnToggle->setIcon(koIcon("view-list-text")); connect(bnToggle, SIGNAL(clicked(bool)), SLOT(toggleLogging(bool))); bnToggle->setChecked(KisConfig(true).readEntry("logviewer_enabled", false)); toggleLogging(KisConfig(true).readEntry("logviewer_enabled", false)); bnClear->setIcon(koIcon("edit-clear")); connect(bnClear, SIGNAL(clicked(bool)), SLOT(clearLog())); bnSave->setIcon(koIcon("document-save")); connect(bnSave, SIGNAL(clicked(bool)), SLOT(saveLog())); bnSettings->setIcon(koIcon("configure")); connect(bnSettings, SIGNAL(clicked(bool)), SLOT(settings())); qRegisterMetaType("QtMsgType"); connect(s_messageSender, SIGNAL(emitMessage(QtMsgType,QString)), this, SLOT(insertMessage(QtMsgType,QString)), Qt::AutoConnection); applyCategories(); changeTheme(); } void LogDockerDock::setCanvas(KoCanvasBase *) { setEnabled(true); } void LogDockerDock::setViewManager(KisViewManager *kisview) { connect(static_cast(kisview->mainWindow()), SIGNAL(themeChanged()), SLOT(changeTheme())); } void LogDockerDock::toggleLogging(bool toggle) { KisConfig(false).writeEntry("logviewer_enabled", toggle); if (toggle) { qInstallMessageHandler(messageHandler); applyCategories(); } else { qInstallMessageHandler(0); } } void LogDockerDock::clearLog() { txtLogViewer->document()->clear(); } void LogDockerDock::saveLog() { KoFileDialog fileDialog(this, KoFileDialog::SaveFile, "logfile"); fileDialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/" + QString("krita_%1.log").arg(QDateTime::currentDateTime().toString())); QString filename = fileDialog.filename(); if (!filename.isEmpty()) { QFile f(filename); f.open(QFile::WriteOnly); f.write(txtLogViewer->document()->toPlainText().toUtf8()); f.close(); } } void LogDockerDock::settings() { KoDialog dlg(this); dlg.setButtons(KoDialog::Ok | KoDialog::Cancel); dlg.setCaption(i18n("Log Settings")); QWidget *page = new QWidget(&dlg); dlg.setMainWidget(page); QVBoxLayout *layout = new QVBoxLayout(page); KConfigGroup cfg( KSharedConfig::openConfig(), "LogDocker"); QCheckBox *chkKrita = new QCheckBox(i18n("General"), page); chkKrita->setChecked(cfg.readEntry("krita_41000", false)); layout->addWidget(chkKrita); QCheckBox *chkResources = new QCheckBox(i18n("Resource Management"), page); chkResources->setChecked(cfg.readEntry("resources_30009", false)); layout->addWidget(chkResources); QCheckBox *chkImage = new QCheckBox(i18n("Image Core"), page); chkImage->setChecked(cfg.readEntry("image_41001", false)); layout->addWidget(chkImage); QCheckBox *chkRegistry = new QCheckBox(i18n("Registries"), page); chkRegistry->setChecked(cfg.readEntry("registry_41002", false)); layout->addWidget(chkRegistry); QCheckBox *chkTools = new QCheckBox(i18n("Tools"), page); chkTools->setChecked(cfg.readEntry("tools_41003", false)); layout->addWidget(chkTools); QCheckBox *chkTiles = new QCheckBox(i18n("Tile Engine"), page); chkTiles->setChecked(cfg.readEntry("tiles_41004", false)); layout->addWidget(chkTiles); QCheckBox *chkFilters = new QCheckBox(i18n("Filters"), page); chkFilters->setChecked(cfg.readEntry("filters_41005", false)); layout->addWidget(chkFilters); QCheckBox *chkPlugins = new QCheckBox(i18n("Plugin Management"), page); chkPlugins->setChecked(cfg.readEntry("plugins_41006", false)); layout->addWidget(chkPlugins); QCheckBox *chkUi = new QCheckBox(i18n("User Interface"), page); chkUi->setChecked(cfg.readEntry("ui_41007", false)); layout->addWidget(chkUi); QCheckBox *chkFile = new QCheckBox(i18n("File loading and saving"), page); chkFile->setChecked(cfg.readEntry("file_41008", false)); layout->addWidget(chkFile); QCheckBox *chkMath = new QCheckBox(i18n("Mathematics and calculations"), page); chkMath->setChecked(cfg.readEntry("math_41009", false)); layout->addWidget(chkMath); QCheckBox *chkRender = new QCheckBox(i18n("Image Rendering"), page); chkRender->setChecked(cfg.readEntry("render_41010", false)); layout->addWidget(chkRender); QCheckBox *chkScript = new QCheckBox(i18n("Scripting"), page); chkScript->setChecked(cfg.readEntry("script_41011", false)); layout->addWidget(chkScript); QCheckBox *chkInput = new QCheckBox(i18n("Input handling"), page); chkInput->setChecked(cfg.readEntry("input_41012", false)); layout->addWidget(chkInput); QCheckBox *chkAction = new QCheckBox(i18n("Actions"), page); chkAction->setChecked(cfg.readEntry("action_41013", false)); layout->addWidget(chkAction); QCheckBox *chkTablet = new QCheckBox(i18n("Tablet Handling"), page); chkTablet->setChecked(cfg.readEntry("tablet_41014", false)); layout->addWidget(chkTablet); QCheckBox *chkOpenGL = new QCheckBox(i18n("GPU Canvas"), page); chkOpenGL->setChecked(cfg.readEntry("opengl_41015", false)); layout->addWidget(chkOpenGL); QCheckBox *chkMetaData = new QCheckBox(i18n("Metadata"), page); chkMetaData->setChecked(cfg.readEntry("metadata_41016", false)); layout->addWidget(chkMetaData); QCheckBox *chkPigment = new QCheckBox(i18n("Color Management"), page); chkPigment->setChecked(cfg.readEntry("pigment", false)); layout->addWidget(chkPigment); if (dlg.exec()) { // Apply the new settings cfg.writeEntry("resources_30009", chkResources->isChecked()); cfg.writeEntry("krita_41000", chkKrita->isChecked()); cfg.writeEntry("image_41001", chkImage->isChecked()); cfg.writeEntry("registry_41002", chkRegistry->isChecked()); cfg.writeEntry("tools_41003", chkTools->isChecked()); cfg.writeEntry("tiles_41004", chkTiles->isChecked()); cfg.writeEntry("filters_41005", chkFilters->isChecked()); cfg.writeEntry("plugins_41006", chkPlugins->isChecked()); cfg.writeEntry("ui_41007", chkUi->isChecked()); cfg.writeEntry("file_41008", chkFile->isChecked()); cfg.writeEntry("math_41009", chkMath->isChecked()); cfg.writeEntry("render_41010", chkRender->isChecked()); cfg.writeEntry("script_41011", chkScript->isChecked()); cfg.writeEntry("input_41012", chkInput->isChecked()); cfg.writeEntry("action_41013", chkAction->isChecked()); cfg.writeEntry("tablet_41014", chkTablet->isChecked()); cfg.writeEntry("opengl_41015", chkOpenGL->isChecked()); cfg.writeEntry("metadata_41016", chkMetaData->isChecked()); cfg.writeEntry("pigment", chkPigment->isChecked()); applyCategories(); } } QString cfgToString(QString tpl, bool cfg) { return tpl.arg(cfg ? "true" : "false"); } void LogDockerDock::applyCategories() { QStringList filters; KConfigGroup cfg( KSharedConfig::openConfig(), "LogDocker"); filters << cfgToString("krita.general=%1", cfg.readEntry("krita_41000", false)); filters << cfgToString("krita.lib.resources=%1", cfg.readEntry("resources_30009", false)); filters << cfgToString("krita.core=%1", cfg.readEntry("image_41001", false)); filters << cfgToString("krita.registry=%1", cfg.readEntry("registry_41002", false)); filters << cfgToString("krita.tools=%1", cfg.readEntry("tools_41003", false)); filters << cfgToString("krita.lib.flake=%1", cfg.readEntry("tools_41003", false)); filters << cfgToString("krita.tiles=%1", cfg.readEntry("tiles_41004", false)); filters << cfgToString("krita.filters=%1", cfg.readEntry("filters_41005", false)); filters << cfgToString("krita.plugins=%1", cfg.readEntry("plugins_41006", false)); filters << cfgToString("krita.lib.plugin=%1", cfg.readEntry("plugins_41006", false)); filters << cfgToString("krita.ui=%1", cfg.readEntry("ui_41007", false)); filters << cfgToString("krita.widgets=%1", cfg.readEntry("ui_41007", false)); filters << cfgToString("krita.widgetutils=%1", cfg.readEntry("ui_41007", false)); filters << cfgToString("krita.file=%1", cfg.readEntry("file_41008", false)); filters << cfgToString("krita.lib.store=%1", cfg.readEntry("file_41008", false)); filters << cfgToString("krita.lib.odf=%1", cfg.readEntry("file_41008", false)); filters << cfgToString("krita.math=%1", cfg.readEntry("math_41009", false)); filters << cfgToString("krita.grender=%1", cfg.readEntry("render_41010", false)); filters << cfgToString("krita.scripting=%1", cfg.readEntry("script_41011", false)); filters << cfgToString("krita.input=%1", cfg.readEntry("input_41012", false)); filters << cfgToString("krita.action=%1", cfg.readEntry("action_41013", false)); filters << cfgToString("krita.tablet=%1", cfg.readEntry("tablet_41014", false)); filters << cfgToString("krita.opengl=%1", cfg.readEntry("opengl_41015", false)); filters << cfgToString("krita.metadata=%1", cfg.readEntry("metadata_41016", false)); filters << cfgToString("krita.lib.pigment=%1", cfg.readEntry("pigment", false)); QLoggingCategory::setFilterRules(filters.join("\n")); } void LogDockerDock::messageHandler(QtMsgType type, const QMessageLogContext &/*context*/, const QString &msg) { s_messageSender->sendMessage(type, msg); } void LogDockerDock::insertMessage(QtMsgType type, const QString &msg) { QTextDocument *doc = txtLogViewer->document(); QTextCursor cursor(doc); cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); cursor.beginEditBlock(); switch (type) { case QtDebugMsg: cursor.insertText(msg + "\n", s_debug); break; case QtInfoMsg: cursor.insertText(msg + "\n", s_info); break; case QtWarningMsg: cursor.insertText(msg + "\n", s_warning); break; case QtCriticalMsg: cursor.insertText(msg + "\n", s_critical); break; case QtFatalMsg: cursor.insertText(msg + "\n", s_fatal); break; } cursor.endEditBlock(); txtLogViewer->verticalScrollBar()->setValue(txtLogViewer->verticalScrollBar()->maximum()); } void LogDockerDock::changeTheme() { clearLog(); QColor background = qApp->palette().background().color(); if (background.value() > 100) { s_debug.setForeground(Qt::black); s_info.setForeground(Qt::darkGreen); s_warning.setForeground(Qt::darkYellow); s_critical.setForeground(Qt::darkRed); s_fatal.setForeground(Qt::darkRed); } else { s_debug.setForeground(Qt::white); s_info.setForeground(Qt::green); s_warning.setForeground(Qt::yellow); s_critical.setForeground(Qt::red); s_fatal.setForeground(Qt::red); } s_fatal.setFontWeight(QFont::Bold); } void MessageSender::sendMessage(QtMsgType type, const QString &msg) { emit emitMessage(type, msg); } diff --git a/plugins/dockers/logdocker/LogDockerDock.h b/plugins/dockers/logdocker/LogDockerDock.h index 4bb749365a..75647661a5 100644 --- a/plugins/dockers/logdocker/LogDockerDock.h +++ b/plugins/dockers/logdocker/LogDockerDock.h @@ -1,75 +1,76 @@ /* * Copyright (c) 2018 Boudewijn Rempt * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * 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 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * 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 Lesser General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _LOGDOCKER_DOCK_H_ #define _LOGDOCKER_DOCK_H_ #include #include #include "ui_WdgLogDocker.h" class MessageSender : public QObject { Q_OBJECT public: MessageSender() : QObject() {} ~MessageSender() override {} void sendMessage(QtMsgType type, const QString &msg); Q_SIGNALS: void emitMessage(QtMsgType type, const QString &msg); }; class LogDockerDock : public QDockWidget, public KisMainwindowObserver, public Ui_WdgLogDocker { Q_OBJECT public: LogDockerDock( ); QString observerName() override { return "LogDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override {} void setViewManager(KisViewManager* kisview) override; private Q_SLOTS: void toggleLogging(bool toggle); void clearLog(); void saveLog(); void settings(); void insertMessage(QtMsgType type, const QString &msg); void changeTheme(); private: void applyCategories(); static MessageSender *s_messageSender; static QTextCharFormat s_debug; static QTextCharFormat s_info; static QTextCharFormat s_warning; static QTextCharFormat s_critical; static QTextCharFormat s_fatal; static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); }; #endif diff --git a/plugins/dockers/lut/lutdocker.cpp b/plugins/dockers/lut/lutdocker.cpp index 78cf0845ff..e00c5a1399 100644 --- a/plugins/dockers/lut/lutdocker.cpp +++ b/plugins/dockers/lut/lutdocker.cpp @@ -1,89 +1,90 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "lutdocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "lutdocker_dock.h" #include #include namespace OCIO = OCIO_NAMESPACE; K_PLUGIN_FACTORY_WITH_JSON(LutDockerPluginFactory, "krita_lutdocker.json", registerPlugin();) class LutDockerDockFactory : public KoDockFactoryBase { public: LutDockerDockFactory() { } QString id() const override { return QString( "LutDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { LutDockerDock * dockWidget = new LutDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: OCIO::ConstConfigRcPtr m_config; }; LutDockerPlugin::LutDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new LutDockerDockFactory()); } LutDockerPlugin::~LutDockerPlugin() { } #include "lutdocker.moc" diff --git a/plugins/dockers/lut/lutdocker.h b/plugins/dockers/lut/lutdocker.h index f51fe9edc3..b965e36ad3 100644 --- a/plugins/dockers/lut/lutdocker.h +++ b/plugins/dockers/lut/lutdocker.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _LUT_DOCKER_H_ #define _LUT_DOCKER_H_ #include #include /** * Template of view plugin */ class LutDockerPlugin : public QObject { Q_OBJECT public: LutDockerPlugin(QObject *parent, const QVariantList &); virtual ~LutDockerPlugin(); }; #endif diff --git a/plugins/dockers/overview/overviewdocker.cpp b/plugins/dockers/overview/overviewdocker.cpp index 173730168c..cebe84a5cf 100644 --- a/plugins/dockers/overview/overviewdocker.cpp +++ b/plugins/dockers/overview/overviewdocker.cpp @@ -1,87 +1,88 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "overviewdocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "overviewdocker_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(OverviewDockerPluginFactory, "krita_overviewdocker.json", registerPlugin();) class OverviewDockerDockFactory : public KoDockFactoryBase { public: OverviewDockerDockFactory() { } QString id() const override { return QString( "OverviewDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { OverviewDockerDock * dockWidget = new OverviewDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; OverviewDockerPlugin::OverviewDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new OverviewDockerDockFactory()); } OverviewDockerPlugin::~OverviewDockerPlugin() { m_view = 0; } #include "overviewdocker.moc" diff --git a/plugins/dockers/overview/overviewdocker.h b/plugins/dockers/overview/overviewdocker.h index 3820926ddf..de453f6616 100644 --- a/plugins/dockers/overview/overviewdocker.h +++ b/plugins/dockers/overview/overviewdocker.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _OVERVIEW_DOCKER_H_ #define _OVERVIEW_DOCKER_H_ #include #include class KisViewManager; /** * Template of view plugin */ class OverviewDockerPlugin : public QObject { Q_OBJECT public: OverviewDockerPlugin(QObject *parent, const QVariantList &); ~OverviewDockerPlugin() override; private: KisViewManager* m_view; }; #endif diff --git a/plugins/dockers/overview/overviewdocker_dock.cpp b/plugins/dockers/overview/overviewdocker_dock.cpp index 18d41ba05e..2cfa57a2bc 100644 --- a/plugins/dockers/overview/overviewdocker_dock.cpp +++ b/plugins/dockers/overview/overviewdocker_dock.cpp @@ -1,153 +1,154 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "overviewdocker_dock.h" #include "overviewwidget.h" #include #include #include #include #include #include #include #include "kis_canvas2.h" #include #include #include #include #include "kis_image.h" #include "kis_paint_device.h" #include "kis_signal_compressor.h" #include "kis_canvas_controller.h" #include "kis_icon_utils.h" #include "kis_signals_blocker.h" OverviewDockerDock::OverviewDockerDock( ) : QDockWidget(i18n("Overview")) , m_zoomSlider(nullptr) , m_rotateSlider(nullptr) , m_mirrorCanvas(nullptr) , m_canvas(nullptr) { QWidget *page = new QWidget(this); m_layout = new QVBoxLayout(page); m_horizontalLayout = new QHBoxLayout(); m_overviewWidget = new OverviewWidget(this); m_overviewWidget->setMinimumHeight(50); m_overviewWidget->setBackgroundRole(QPalette::AlternateBase); m_overviewWidget->setAutoFillBackground(true); // paints background role before paint() m_layout->addWidget(m_overviewWidget, 1); setWidget(page); } void OverviewDockerDock::setCanvas(KoCanvasBase * canvas) { if(m_canvas == canvas) return; setEnabled(canvas != nullptr); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_canvas->image()->disconnect(this); } if (m_zoomSlider) { m_layout->removeWidget(m_zoomSlider); delete m_zoomSlider; m_zoomSlider = nullptr; } if (m_rotateSlider) { m_horizontalLayout->removeWidget(m_rotateSlider); delete m_rotateSlider; m_rotateSlider = nullptr; } if (m_mirrorCanvas) { m_horizontalLayout->removeWidget(m_mirrorCanvas); delete m_mirrorCanvas; m_mirrorCanvas = nullptr; } m_layout->removeItem(m_horizontalLayout); m_canvas = dynamic_cast(canvas); m_overviewWidget->setCanvas(canvas); if (m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->zoomController() && m_canvas->viewManager()->zoomController()->zoomAction()) { m_zoomSlider = m_canvas->viewManager()->zoomController()->zoomAction()->createWidget(m_canvas->imageView()->KisView::statusBar()); m_layout->addWidget(m_zoomSlider); m_rotateSlider = new KisDoubleSliderSpinBox(); m_rotateSlider->setRange(-180, 180, 2); m_rotateSlider->setValue(m_canvas->rotationAngle()); m_rotateSlider->setPrefix(i18n("Rotation: ")); m_rotateSlider->setSuffix("°"); connect(m_rotateSlider, SIGNAL(valueChanged(qreal)), this, SLOT(rotateCanvasView(qreal)), Qt::UniqueConnection); connect(m_canvas->canvasController()->proxyObject, SIGNAL(canvasOffsetXChanged(int)), this, SLOT(updateSlider())); m_mirrorCanvas = new QToolButton(); QList actions = m_canvas->viewManager()->actionCollection()->actions(); Q_FOREACH(QAction* action, actions) { if (action->objectName()=="mirror_canvas") { m_mirrorCanvas->setDefaultAction(action); } } m_horizontalLayout->addWidget(m_mirrorCanvas); m_horizontalLayout->addWidget(m_rotateSlider); m_layout->addLayout(m_horizontalLayout); } } void OverviewDockerDock::unsetCanvas() { setEnabled(false); m_canvas = nullptr; m_overviewWidget->unsetCanvas(); } void OverviewDockerDock::rotateCanvasView(qreal rotation) { KisCanvasController *canvasController = dynamic_cast(m_canvas->viewManager()->canvasBase()->canvasController()); if (canvasController) { canvasController->rotateCanvas(rotation-m_canvas->rotationAngle()); } } void OverviewDockerDock::updateSlider() { KisSignalsBlocker l(m_rotateSlider); qreal rotation = m_canvas->rotationAngle(); if (rotation > 180) { rotation = rotation - 360; } else if (rotation < -180) { rotation = rotation + 360; } if (m_rotateSlider->value() != rotation) { m_rotateSlider->setValue(rotation); } } diff --git a/plugins/dockers/overview/overviewdocker_dock.h b/plugins/dockers/overview/overviewdocker_dock.h index 0270b04478..2fbc1f6d2e 100644 --- a/plugins/dockers/overview/overviewdocker_dock.h +++ b/plugins/dockers/overview/overviewdocker_dock.h @@ -1,56 +1,57 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _OVERVIEW_DOCK_H_ #define _OVERVIEW_DOCK_H_ #include #include #include #include #include class QVBoxLayout; class QHBoxLayout; class QToolButton; class OverviewWidget; class OverviewDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: OverviewDockerDock(); QString observerName() override { return "OverviewDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; public Q_SLOTS: void rotateCanvasView(qreal rotation); void updateSlider(); private: QVBoxLayout *m_layout; QHBoxLayout *m_horizontalLayout; OverviewWidget *m_overviewWidget; QWidget *m_zoomSlider; KisDoubleSliderSpinBox *m_rotateSlider; QToolButton *m_mirrorCanvas; QPointer m_canvas; }; #endif diff --git a/plugins/dockers/overview/overviewwidget.cc b/plugins/dockers/overview/overviewwidget.cc index 20c4bcf926..201c60a13e 100644 --- a/plugins/dockers/overview/overviewwidget.cc +++ b/plugins/dockers/overview/overviewwidget.cc @@ -1,381 +1,382 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "overviewwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_idle_watcher.h" #include "krita_utils.h" #include "kis_painter.h" #include #include "kis_transform_worker.h" #include "kis_filter_strategy.h" #include #include const qreal oversample = 2.; const int thumbnailTileDim = 128; struct OverviewThumbnailStrokeStrategy::Private { class ProcessData : public KisStrokeJobData { public: ProcessData(KisPaintDeviceSP _dev, KisPaintDeviceSP _thumbDev, const QSize& _thumbnailSize, const QRect &_rect) : KisStrokeJobData(CONCURRENT), dev(_dev), thumbDev(_thumbDev), thumbnailSize(_thumbnailSize), tileRect(_rect) {} KisPaintDeviceSP dev; KisPaintDeviceSP thumbDev; QSize thumbnailSize; QRect tileRect; }; class FinishProcessing : public KisStrokeJobData { public: FinishProcessing(KisPaintDeviceSP _thumbDev, const QSize& _thumbnailSize) : KisStrokeJobData(SEQUENTIAL), thumbDev(_thumbDev), thumbnailSize(_thumbnailSize) {} KisPaintDeviceSP thumbDev; QSize thumbnailSize; }; }; OverviewWidget::OverviewWidget(QWidget * parent) : QWidget(parent) , m_canvas(0) , m_dragging(false) , m_imageIdleWatcher(250) { setMouseTracking(true); KisConfig cfg(true); slotThemeChanged(); } OverviewWidget::~OverviewWidget() { } void OverviewWidget::setCanvas(KoCanvasBase * canvas) { if (m_canvas) { m_canvas->image()->disconnect(this); } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_imageIdleWatcher.setTrackedImage(m_canvas->image()); connect(&m_imageIdleWatcher, &KisIdleWatcher::startedIdleMode, this, &OverviewWidget::generateThumbnail); connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)),SLOT(startUpdateCanvasProjection())); connect(m_canvas->image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),SLOT(startUpdateCanvasProjection())); connect(m_canvas->canvasController()->proxyObject, SIGNAL(canvasOffsetXChanged(int)), this, SLOT(update()), Qt::UniqueConnection); connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotThemeChanged())); generateThumbnail(); } } QSize OverviewWidget::recalculatePreviewSize() { QSize imageSize(m_canvas->image()->bounds().size()); const qreal hScale = 1.0 * this->width() / imageSize.width(); const qreal vScale = 1.0 * this->height() / imageSize.height(); m_previewScale = qMin(hScale, vScale); return imageSize * m_previewScale; } QPointF OverviewWidget::previewOrigin() { const QSize previewSize = recalculatePreviewSize(); return QPointF((width() - previewSize.width()) / 2.0f, (height() - previewSize.height()) / 2.0f); } QPolygonF OverviewWidget::previewPolygon() { if (m_canvas) { const QRectF &canvasRect = QRectF(m_canvas->canvasWidget()->rect()); return canvasToPreviewTransform().map(canvasRect); } return QPolygonF(); } QTransform OverviewWidget::previewToCanvasTransform() { QTransform previewToImage = QTransform::fromTranslate(-this->width() / 2.0, -this->height() / 2.0) * QTransform::fromScale(1.0 / m_previewScale, 1.0 / m_previewScale) * QTransform::fromTranslate(m_canvas->image()->width() / 2.0, m_canvas->image()->height() / 2.0); return previewToImage * m_canvas->coordinatesConverter()->imageToWidgetTransform(); } QTransform OverviewWidget::canvasToPreviewTransform() { return previewToCanvasTransform().inverted(); } void OverviewWidget::startUpdateCanvasProjection() { m_imageIdleWatcher.startCountdown(); } void OverviewWidget::showEvent(QShowEvent *event) { Q_UNUSED(event); m_imageIdleWatcher.startCountdown(); } void OverviewWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); if (m_canvas) { if (!m_oldPixmap.isNull()) { QSize newSize = recalculatePreviewSize(); m_pixmap = m_oldPixmap.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } m_imageIdleWatcher.startCountdown(); } } void OverviewWidget::mousePressEvent(QMouseEvent* event) { if (m_canvas) { QPointF previewPos = event->pos(); if (!previewPolygon().containsPoint(previewPos, Qt::WindingFill)) { const QRect& canvasRect = m_canvas->canvasWidget()->rect(); const QPointF newCanvasPos = previewToCanvasTransform().map(previewPos) - QPointF(canvasRect.width() / 2.0f, canvasRect.height() / 2.0f); m_canvas->canvasController()->pan(newCanvasPos.toPoint()); } m_lastPos = previewPos; m_dragging = true; } event->accept(); update(); } void OverviewWidget::mouseMoveEvent(QMouseEvent* event) { if (m_dragging) { QPointF previewPos = event->pos(); const QPointF lastCanvasPos = previewToCanvasTransform().map(m_lastPos); const QPointF newCanvasPos = previewToCanvasTransform().map(event->pos()); QPointF diff = newCanvasPos - lastCanvasPos; m_canvas->canvasController()->pan(diff.toPoint()); m_lastPos = previewPos; } event->accept(); } void OverviewWidget::mouseReleaseEvent(QMouseEvent* event) { m_dragging = false; event->accept(); update(); } void OverviewWidget::wheelEvent(QWheelEvent* event) { float delta = event->delta(); if (delta > 0) { m_canvas->viewManager()->zoomController()->zoomAction()->zoomIn(); } else { m_canvas->viewManager()->zoomController()->zoomAction()->zoomOut(); } } void OverviewWidget::generateThumbnail() { if (isVisible()) { QMutexLocker locker(&mutex); if (m_canvas) { QSize previewSize = recalculatePreviewSize(); if(previewSize.isValid()){ KisImageSP image = m_canvas->image(); if (!strokeId.isNull()) { image->cancelStroke(strokeId); strokeId.clear(); } OverviewThumbnailStrokeStrategy* stroke = new OverviewThumbnailStrokeStrategy(image); connect(stroke, SIGNAL(thumbnailUpdated(QImage)), this, SLOT(updateThumbnail(QImage))); strokeId = image->startStroke(stroke); KisPaintDeviceSP dev = image->projection(); KisPaintDeviceSP thumbDev = new KisPaintDevice(dev->colorSpace()); //creating a special stroke that computes thumbnail image in small chunks that can be quickly interrupted //if user starts painting QList jobs = OverviewThumbnailStrokeStrategy::createJobsData(dev, image->bounds(), thumbDev, previewSize); Q_FOREACH (KisStrokeJobData *jd, jobs) { image->addJob(strokeId, jd); } image->endStroke(strokeId); } } } } void OverviewWidget::updateThumbnail(QImage pixmap) { m_pixmap = QPixmap::fromImage(pixmap); m_oldPixmap = m_pixmap.copy(); update(); } void OverviewWidget::slotThemeChanged() { m_outlineColor = qApp->palette().color(QPalette::Highlight); } void OverviewWidget::paintEvent(QPaintEvent* event) { QWidget::paintEvent(event); if (m_canvas) { QPainter p(this); const QSize previewSize = recalculatePreviewSize(); const QRectF previewRect = QRectF(previewOrigin(), previewSize); p.drawPixmap(previewRect.toRect(), m_pixmap); QRect r = rect(); QPolygonF outline; outline << r.topLeft() << r.topRight() << r.bottomRight() << r.bottomLeft(); QPen pen; pen.setColor(m_outlineColor); pen.setStyle(Qt::DashLine); p.setPen(pen); p.drawPolygon(outline.intersected(previewPolygon())); pen.setStyle(Qt::SolidLine); p.setPen(pen); p.drawPolygon(previewPolygon()); } } OverviewThumbnailStrokeStrategy::OverviewThumbnailStrokeStrategy(KisImageWSP image) : KisSimpleStrokeStrategy("OverviewThumbnail"), m_image(image) { enableJob(KisSimpleStrokeStrategy::JOB_INIT, true, KisStrokeJobData::BARRIER, KisStrokeJobData::EXCLUSIVE); enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE); //enableJob(KisSimpleStrokeStrategy::JOB_FINISH); enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); setRequestsOtherStrokesToEnd(false); setClearsRedoOnStart(false); setCanForgetAboutMe(true); } QList OverviewThumbnailStrokeStrategy::createJobsData(KisPaintDeviceSP dev, const QRect& imageRect, KisPaintDeviceSP thumbDev, const QSize& thumbnailSize) { QSize thumbnailOversampledSize = oversample * thumbnailSize; if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) { thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio); } QVector tileRects = KritaUtils::splitRectIntoPatches(QRect(QPoint(0, 0), thumbnailOversampledSize), QSize(thumbnailTileDim, thumbnailTileDim)); QList jobsData; Q_FOREACH (const QRect &tileRectangle, tileRects) { jobsData << new OverviewThumbnailStrokeStrategy::Private::ProcessData(dev, thumbDev, thumbnailOversampledSize, tileRectangle); } jobsData << new OverviewThumbnailStrokeStrategy::Private::FinishProcessing(thumbDev, thumbnailSize); return jobsData; } OverviewThumbnailStrokeStrategy::~OverviewThumbnailStrokeStrategy() { } void OverviewThumbnailStrokeStrategy::initStrokeCallback() { } void OverviewThumbnailStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { Private::ProcessData *d_pd = dynamic_cast(data); if (d_pd) { //we aren't going to use oversample capability of createThumbnailDevice because it recomputes exact bounds for each small patch, which is //slow. We'll handle scaling separately. KisPaintDeviceSP thumbnailTile = d_pd->dev->createThumbnailDeviceOversampled(d_pd->thumbnailSize.width(), d_pd->thumbnailSize.height(), 1, m_image->bounds(), d_pd->tileRect); { QMutexLocker locker(&m_thumbnailMergeMutex); KisPainter gc(d_pd->thumbDev); gc.bitBlt(QPoint(d_pd->tileRect.x(), d_pd->tileRect.y()), thumbnailTile, d_pd->tileRect); } return; } Private::FinishProcessing *d_fp = dynamic_cast(data); if (d_fp) { QImage overviewImage; KoDummyUpdater updater; KisTransformWorker worker(d_fp->thumbDev, 1 / oversample, 1 / oversample, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, &updater, KisFilterStrategyRegistry::instance()->value("Bilinear")); worker.run(); overviewImage = d_fp->thumbDev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), QRect(QPoint(0,0), d_fp->thumbnailSize)); emit thumbnailUpdated(overviewImage); return; } } void OverviewThumbnailStrokeStrategy::finishStrokeCallback() { } void OverviewThumbnailStrokeStrategy::cancelStrokeCallback() { } diff --git a/plugins/dockers/overview/overviewwidget.h b/plugins/dockers/overview/overviewwidget.h index ff69aba503..b52c60d2d2 100644 --- a/plugins/dockers/overview/overviewwidget.h +++ b/plugins/dockers/overview/overviewwidget.h @@ -1,117 +1,118 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef OVERVIEWWIDGET_H #define OVERVIEWWIDGET_H #include #include #include #include #include #include "kis_idle_watcher.h" #include "kis_simple_stroke_strategy.h" #include class KisSignalCompressor; class KoCanvasBase; class OverviewThumbnailStrokeStrategy : public QObject, public KisSimpleStrokeStrategy { Q_OBJECT public: OverviewThumbnailStrokeStrategy(KisImageWSP image); ~OverviewThumbnailStrokeStrategy() override; static QList createJobsData(KisPaintDeviceSP dev, const QRect& imageRect, KisPaintDeviceSP thumbDev, const QSize &thumbnailSize); private: void initStrokeCallback() override; void doStrokeCallback(KisStrokeJobData *data) override; void finishStrokeCallback() override; void cancelStrokeCallback() override; Q_SIGNALS: //Emitted when thumbnail is updated and overviewImage is fully generated. void thumbnailUpdated(QImage pixmap); private: struct Private; const QScopedPointer m_d; QMutex m_thumbnailMergeMutex; KisImageSP m_image; }; class OverviewWidget : public QWidget { Q_OBJECT public: OverviewWidget(QWidget * parent = 0); ~OverviewWidget() override; virtual void setCanvas(KoCanvasBase *canvas); virtual void unsetCanvas() { m_canvas = 0; } public Q_SLOTS: void startUpdateCanvasProjection(); void generateThumbnail(); void updateThumbnail(QImage pixmap); void slotThemeChanged(); protected: void resizeEvent(QResizeEvent *event) override; void showEvent(QShowEvent *event) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void wheelEvent(QWheelEvent* event) override; private: QSize recalculatePreviewSize(); QPointF previewOrigin(); QTransform canvasToPreviewTransform(); QTransform previewToCanvasTransform(); QPolygonF previewPolygon(); qreal m_previewScale; QPixmap m_oldPixmap; QPixmap m_pixmap; QPointer m_canvas; bool m_dragging; QPointF m_lastPos; QColor m_outlineColor; KisIdleWatcher m_imageIdleWatcher; KisStrokeId strokeId; QMutex mutex; }; #endif /* OVERVIEWWIDGET_H */ diff --git a/plugins/dockers/palettedocker/palettedocker.h b/plugins/dockers/palettedocker/palettedocker.h index 5b6c082b39..3d9745a8a0 100644 --- a/plugins/dockers/palettedocker/palettedocker.h +++ b/plugins/dockers/palettedocker/palettedocker.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2013 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PALETTEDOCKER_H #define PALETTEDOCKER_H #include #include /** * Docker showing the channels of the current layer */ class PaletteDockerPlugin : public QObject { Q_OBJECT public: PaletteDockerPlugin(QObject *parent, const QVariantList &); ~PaletteDockerPlugin() override; }; #endif diff --git a/plugins/dockers/patterndocker/patterndocker.cpp b/plugins/dockers/patterndocker/patterndocker.cpp index cb22ea5f96..ff7c59e587 100644 --- a/plugins/dockers/patterndocker/patterndocker.cpp +++ b/plugins/dockers/patterndocker/patterndocker.cpp @@ -1,87 +1,88 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "patterndocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "patterndocker_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(PatternDockerPluginFactory, "krita_patterndocker.json", registerPlugin();) class PatternDockerDockFactory : public KoDockFactoryBase { public: PatternDockerDockFactory() { } QString id() const override { return QString( "PatternDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { PatternDockerDock * dockWidget = new PatternDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; PatternDockerPlugin::PatternDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new PatternDockerDockFactory()); } PatternDockerPlugin::~PatternDockerPlugin() { m_view = 0; } #include "patterndocker.moc" diff --git a/plugins/dockers/patterndocker/patterndocker.h b/plugins/dockers/patterndocker/patterndocker.h index 1d4b908b10..0c440d052a 100644 --- a/plugins/dockers/patterndocker/patterndocker.h +++ b/plugins/dockers/patterndocker/patterndocker.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PATTERN_DOCKER_H_ #define _PATTERN_DOCKER_H_ #include #include class KisViewManager; /** * Template of view plugin */ class PatternDockerPlugin : public QObject { Q_OBJECT public: PatternDockerPlugin(QObject *parent, const QVariantList &); ~PatternDockerPlugin() override; private: KisViewManager* m_view; }; #endif diff --git a/plugins/dockers/patterndocker/patterndocker_dock.cpp b/plugins/dockers/patterndocker/patterndocker_dock.cpp index 4fdc6c5566..2ddd60a1c3 100644 --- a/plugins/dockers/patterndocker/patterndocker_dock.cpp +++ b/plugins/dockers/patterndocker/patterndocker_dock.cpp @@ -1,69 +1,70 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "patterndocker_dock.h" #include #include #include #include #include #include #include PatternDockerDock::PatternDockerDock( ) : QDockWidget(i18n("Patterns")) { m_patternChooser = new KisPatternChooser(this); m_patternChooser->setPreviewOrientation(Qt::Vertical); m_patternChooser->setCurrentItem(0,0); m_patternChooser->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); m_patternChooser->setMinimumHeight(160); setWidget(m_patternChooser); } void PatternDockerDock::setViewManager(KisViewManager* kisview) { KisCanvasResourceProvider* resourceProvider = kisview->canvasResourceProvider(); connect(resourceProvider, SIGNAL(sigPatternChanged(KoPatternSP)), this, SLOT(patternChanged(KoPatternSP))); connect(m_patternChooser, SIGNAL(resourceSelected(KoResourceSP )), resourceProvider, SLOT(slotPatternActivated(KoResourceSP ))); } void PatternDockerDock::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); } void PatternDockerDock::unsetCanvas() { setEnabled(false); } void PatternDockerDock::patternChanged(KoPatternSP pattern) { m_patternChooser->setCurrentPattern(pattern); } diff --git a/plugins/dockers/patterndocker/patterndocker_dock.h b/plugins/dockers/patterndocker/patterndocker_dock.h index ad58706ec6..a6eaa6aeb9 100644 --- a/plugins/dockers/patterndocker/patterndocker_dock.h +++ b/plugins/dockers/patterndocker/patterndocker_dock.h @@ -1,47 +1,48 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PATTERN_DOCK_H_ #define _PATTERN_DOCK_H_ #include #include #include class KisPatternChooser; class PatternDockerDock : public QDockWidget, public KisMainwindowObserver { Q_OBJECT public: PatternDockerDock( ); void setViewManager(KisViewManager* kisview) override; void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; QString observerName() override { return "PatternDockerDock"; } public Q_SLOTS: void patternChanged(KoPatternSP pattern); private Q_SLOTS: private: KisPatternChooser* m_patternChooser; }; #endif diff --git a/plugins/dockers/presetdocker/presetdocker.cpp b/plugins/dockers/presetdocker/presetdocker.cpp index b6870de8d0..47547d0feb 100644 --- a/plugins/dockers/presetdocker/presetdocker.cpp +++ b/plugins/dockers/presetdocker/presetdocker.cpp @@ -1,87 +1,88 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "presetdocker.h" #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "presetdocker_dock.h" #include K_PLUGIN_FACTORY_WITH_JSON(PresetDockerPluginFactory, "krita_presetdocker.json", registerPlugin();) class PresetDockerDockFactory : public KoDockFactoryBase { public: PresetDockerDockFactory() { } QString id() const override { return QString( "PresetDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { PresetDockerDock * dockWidget = new PresetDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; PresetDockerPlugin::PresetDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new PresetDockerDockFactory()); } PresetDockerPlugin::~PresetDockerPlugin() { m_view = 0; } #include "presetdocker.moc" diff --git a/plugins/dockers/presetdocker/presetdocker.h b/plugins/dockers/presetdocker/presetdocker.h index 0635425c62..ab8cf1ffa5 100644 --- a/plugins/dockers/presetdocker/presetdocker.h +++ b/plugins/dockers/presetdocker/presetdocker.h @@ -1,39 +1,40 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PRESET_DOCKER_H_ #define _PRESET_DOCKER_H_ #include #include class KisViewManager; /** * Template of view plugin */ class PresetDockerPlugin : public QObject { Q_OBJECT public: PresetDockerPlugin(QObject *parent, const QVariantList &); ~PresetDockerPlugin() override; private: KisViewManager* m_view; }; #endif diff --git a/plugins/dockers/presetdocker/presetdocker_dock.cpp b/plugins/dockers/presetdocker/presetdocker_dock.cpp index edaba9291d..1374389fe1 100644 --- a/plugins/dockers/presetdocker/presetdocker_dock.cpp +++ b/plugins/dockers/presetdocker/presetdocker_dock.cpp @@ -1,85 +1,86 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "presetdocker_dock.h" #include #include #include #include #include #include "kis_canvas2.h" #include "KisViewManager.h" #include "kis_paintop_box.h" #include "kis_paintop_presets_chooser_popup.h" #include "kis_canvas_resource_provider.h" #include PresetDockerDock::PresetDockerDock( ) : QDockWidget(i18n("Brush Presets")) , m_canvas(0) { m_presetChooser = new KisPaintOpPresetsChooserPopup(this); m_presetChooser->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); m_presetChooser->showButtons(false); setWidget(m_presetChooser); } void PresetDockerDock::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_presetChooser->disconnect(m_canvas->viewManager()->paintOpBox()); } m_canvas = dynamic_cast(canvas); if (!m_canvas || !m_canvas->viewManager() || !m_canvas->resourceManager()) return; connect(m_presetChooser, SIGNAL(resourceSelected(KoResourceSP )), m_canvas->viewManager()->paintOpBox(), SLOT(resourceSelected(KoResourceSP ))); connect(m_presetChooser, SIGNAL(resourceClicked(KoResourceSP )), m_canvas->viewManager()->paintOpBox(), SLOT(resourceSelected(KoResourceSP ))); connect(canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(canvasResourceChanged(int,QVariant))); connect(m_canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), m_presetChooser, SLOT(slotThemeChanged())); canvasResourceChanged(); } void PresetDockerDock::canvasResourceChanged(int /*key*/, const QVariant& /*v*/) { if (m_canvas && m_canvas->resourceManager()) { if (sender()) sender()->blockSignals(true); KisPaintOpPresetSP preset = m_canvas->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if(preset) m_presetChooser->canvasResourceChanged(preset); if (sender()) sender()->blockSignals(false); m_presetChooser->updateViewSettings(); } } diff --git a/plugins/dockers/presetdocker/presetdocker_dock.h b/plugins/dockers/presetdocker/presetdocker_dock.h index 6d77ef2289..9ae08a239a 100644 --- a/plugins/dockers/presetdocker/presetdocker_dock.h +++ b/plugins/dockers/presetdocker/presetdocker_dock.h @@ -1,43 +1,44 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PRESETDOCKER_DOCK_H_ #define _PRESETDOCKER_DOCK_H_ #include #include #include #include class KisPaintOpPresetsChooserPopup; class PresetDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: PresetDockerDock( ); QString observerName() override { return "PresetDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override { m_canvas = 0; setEnabled(false);} public Q_SLOTS: void canvasResourceChanged(int key = 0, const QVariant& v = QVariant()); private: QPointer m_canvas; KisPaintOpPresetsChooserPopup* m_presetChooser; }; #endif diff --git a/plugins/dockers/presethistory/presethistory.cpp b/plugins/dockers/presethistory/presethistory.cpp index b2e67bc232..0a40a024a1 100644 --- a/plugins/dockers/presethistory/presethistory.cpp +++ b/plugins/dockers/presethistory/presethistory.cpp @@ -1,70 +1,71 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "presethistory.h" #include #include #include #include "presethistory_dock.h" K_PLUGIN_FACTORY_WITH_JSON(PresetHistoryPluginFactory, "krita_presethistory.json", registerPlugin();) class PresetHistoryDockFactory : public KoDockFactoryBase { public: PresetHistoryDockFactory() { } QString id() const override { return QString( "PresetHistory" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { PresetHistoryDock * dockWidget = new PresetHistoryDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } }; PresetHistoryPlugin::PresetHistoryPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new PresetHistoryDockFactory()); } PresetHistoryPlugin::~PresetHistoryPlugin() { } #include "presethistory.moc" diff --git a/plugins/dockers/presethistory/presethistory.h b/plugins/dockers/presethistory/presethistory.h index 41cfc3f59a..70c12efdbc 100644 --- a/plugins/dockers/presethistory/presethistory.h +++ b/plugins/dockers/presethistory/presethistory.h @@ -1,35 +1,36 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PRESET_DOCKER_H_ #define _PRESET_DOCKER_H_ #include #include /** * Shows the last used presets */ class PresetHistoryPlugin : public QObject { Q_OBJECT public: PresetHistoryPlugin(QObject *parent, const QVariantList &); ~PresetHistoryPlugin() override; }; #endif diff --git a/plugins/dockers/presethistory/presethistory_dock.cpp b/plugins/dockers/presethistory/presethistory_dock.cpp index bead0764ae..f6457053b1 100644 --- a/plugins/dockers/presethistory/presethistory_dock.cpp +++ b/plugins/dockers/presethistory/presethistory_dock.cpp @@ -1,150 +1,151 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "presethistory_dock.h" #include #include #include #include #include #include #include #include "kis_config.h" #include "kis_canvas2.h" #include "KisViewManager.h" #include "kis_paintop_box.h" #include "kis_paintop_presets_chooser_popup.h" #include "kis_canvas_resource_provider.h" #include "KisResourceServerProvider.h" #include #include #include #define ICON_SIZE 48 PresetHistoryDock::PresetHistoryDock( ) : QDockWidget(i18n("Brush Preset History")) , m_canvas(0) , m_block(false) , m_initialized(false) { m_presetHistory = new QListWidget(this); m_presetHistory->setIconSize(QSize(ICON_SIZE, ICON_SIZE)); m_presetHistory->setDragEnabled(false); m_presetHistory->setSelectionBehavior(QAbstractItemView::SelectRows); m_presetHistory->setSelectionMode(QAbstractItemView::SingleSelection); m_presetHistory->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); setWidget(m_presetHistory); QScroller* scroller = KisKineticScroller::createPreconfiguredScroller(m_presetHistory); if( scroller ) { connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State))); } connect(m_presetHistory, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(presetSelected(QListWidgetItem*))); } void PresetHistoryDock::setCanvas(KoCanvasBase * canvas) { setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); disconnect(m_canvas->resourceManager()); } m_canvas = dynamic_cast(canvas); if (!m_canvas || !m_canvas->viewManager() || !m_canvas->resourceManager()) return; connect(canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), SLOT(canvasResourceChanged(int,QVariant))); if (!m_initialized) { KisConfig cfg(true); QStringList presetHistory = cfg.readEntry("presethistory", "").split(",", QString::SkipEmptyParts); KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (const QString &p, presetHistory) { KisPaintOpPresetSP preset = rserver->resourceByName(p); addPreset(preset); } m_initialized = true; } } void PresetHistoryDock::unsetCanvas() { m_canvas = 0; setEnabled(false); QStringList presetHistory; for(int i = m_presetHistory->count() -1; i >=0; --i) { QListWidgetItem *item = m_presetHistory->item(i); QVariant v = item->data(Qt::UserRole); KisPaintOpPresetSP preset = v.value(); presetHistory << preset->name(); } KisConfig cfg(false); cfg.writeEntry("presethistory", presetHistory.join(",")); } void PresetHistoryDock::presetSelected(QListWidgetItem *item) { if (item) { QVariant v = item->data(Qt::UserRole); KisPaintOpPresetSP preset = v.value(); m_block = true; m_canvas->viewManager()->paintOpBox()->resourceSelected(preset); m_block = false; } } void PresetHistoryDock::canvasResourceChanged(int key, const QVariant& /*v*/) { if (m_block) return; if (m_canvas && key == KisCanvasResourceProvider::CurrentPaintOpPreset) { KisPaintOpPresetSP preset = m_canvas->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset) { for (int i = 0; i < m_presetHistory->count(); ++i) { if (preset->name() == m_presetHistory->item(i)->text()) { m_presetHistory->setCurrentRow(i); return; } } addPreset(preset); } } } void PresetHistoryDock::addPreset(KisPaintOpPresetSP preset) { if (preset) { QListWidgetItem *item = new QListWidgetItem(QPixmap::fromImage(preset->image()), preset->name()); QVariant v = QVariant::fromValue(preset); item->setData(Qt::UserRole, v); m_presetHistory->insertItem(0, item); m_presetHistory->setCurrentRow(0); if (m_presetHistory->count() > 10) { m_presetHistory->takeItem(10); } } } diff --git a/plugins/dockers/presethistory/presethistory_dock.h b/plugins/dockers/presethistory/presethistory_dock.h index b25e54d229..7af9674812 100644 --- a/plugins/dockers/presethistory/presethistory_dock.h +++ b/plugins/dockers/presethistory/presethistory_dock.h @@ -1,55 +1,56 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _PRESETHISTORY_DOCK_H_ #define _PRESETHISTORY_DOCK_H_ #include #include #include #include #include #include class QListWidget; class QListWidgetItem; class PresetHistoryDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: PresetHistoryDock(); QString observerName() override { return "PresetHistoryDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; public Q_SLOTS: void slotScrollerStateChanged(QScroller::State state){ KisKineticScroller::updateCursor(this, state); } private Q_SLOTS: void presetSelected(QListWidgetItem* item); void canvasResourceChanged(int key, const QVariant& v); private: void addPreset(KisPaintOpPresetSP preset); private: QPointer m_canvas; QListWidget *m_presetHistory; bool m_block; bool m_initialized; }; #endif diff --git a/plugins/dockers/smallcolorselector/kis_small_color_widget.cc b/plugins/dockers/smallcolorselector/kis_small_color_widget.cc index db47426e6d..db65f16ec6 100644 --- a/plugins/dockers/smallcolorselector/kis_small_color_widget.cc +++ b/plugins/dockers/smallcolorselector/kis_small_color_widget.cc @@ -1,516 +1,517 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_small_color_widget.h" #include #include "kis_slider_spin_box.h" #include #include "kis_signal_compressor.h" #include #include #include #include "kis_debug.h" #include "kis_assert.h" #include #include "KisGLImageF16.h" #include "KisGLImageWidget.h" #include "KisClickableGLImageWidget.h" #include "kis_display_color_converter.h" #include "kis_signal_auto_connection.h" #include "kis_signal_compressor_with_param.h" #include #include #include "kis_fixed_paint_device.h" #include struct KisSmallColorWidget::Private { qreal hue; // 0 ... 1.0 qreal value; // 0 ... 1.0 qreal saturation; // 0 ... 1.0 bool updateAllowed; KisClickableGLImageWidget *hueWidget; KisClickableGLImageWidget *valueWidget; KisSignalCompressor *repaintCompressor; KisSignalCompressor *resizeUpdateCompressor; KisSignalCompressor *valueSliderUpdateCompressor; KisSignalCompressor *colorChangedSignalCompressor; KisSignalCompressorWithParam *dynamicRangeCompressor; int huePreferredHeight = 32; KisSliderSpinBox *dynamicRange = 0; qreal currentRelativeDynamicRange = 1.0; KisDisplayColorConverter *displayColorConverter = KisDisplayColorConverter::dumbConverterInstance(); KisSignalAutoConnectionsStore colorConverterConnections; bool hasHDR = false; bool hasHardwareHDR = false; qreal effectiveRelativeDynamicRange() const { return hasHDR ? currentRelativeDynamicRange : 1.0; } const KoColorSpace *outputColorSpace() { return KoColorSpaceRegistry::instance()-> colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), displayColorConverter->openGLCanvasSurfaceProfile()); } const KoColorSpace *generationColorSpace() { const KoColorSpace *result = displayColorConverter->paintingColorSpace(); if (!result || result->colorModelId() != RGBAColorModelID) { result = outputColorSpace(); } else if (result->colorDepthId() != Float32BitsColorDepthID) { result = KoColorSpaceRegistry::instance()-> colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), result->profile()); } // PQ color space we deliniearize into linear one if (result->colorModelId() == RGBAColorModelID && result->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId()) { result = KoColorSpaceRegistry::instance()-> colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), KoColorSpaceRegistry::instance()->p2020G10Profile()); } return result; } }; KisSmallColorWidget::KisSmallColorWidget(QWidget* parent) : QWidget(parent), d(new Private) { d->hue = 0.0; d->value = 0; d->saturation = 0; d->updateAllowed = true; d->repaintCompressor = new KisSignalCompressor(20, KisSignalCompressor::FIRST_ACTIVE, this); connect(d->repaintCompressor, SIGNAL(timeout()), SLOT(update())); d->resizeUpdateCompressor = new KisSignalCompressor(200, KisSignalCompressor::FIRST_ACTIVE, this); connect(d->resizeUpdateCompressor, SIGNAL(timeout()), SLOT(slotUpdatePalettes())); d->valueSliderUpdateCompressor = new KisSignalCompressor(100, KisSignalCompressor::FIRST_ACTIVE, this); connect(d->valueSliderUpdateCompressor, SIGNAL(timeout()), SLOT(updateSVPalette())); d->colorChangedSignalCompressor = new KisSignalCompressor(20, KisSignalCompressor::FIRST_ACTIVE, this); connect(d->colorChangedSignalCompressor, SIGNAL(timeout()), SLOT(slotTellColorChanged())); { using namespace std::placeholders; std::function callback( std::bind(&KisSmallColorWidget::updateDynamicRange, this, _1)); d->dynamicRangeCompressor = new KisSignalCompressorWithParam(50, callback); } const KisSurfaceColorSpace colorSpace = KisSurfaceColorSpace::DefaultColorSpace; d->hueWidget = new KisClickableGLImageWidget(colorSpace, this); d->hueWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); d->hueWidget->setHandlePaintingStrategy(new KisClickableGLImageWidget::VerticalLineHandleStrategy); connect(d->hueWidget, SIGNAL(selected(const QPointF&)), SLOT(slotHueSliderChanged(const QPointF&))); d->valueWidget = new KisClickableGLImageWidget(colorSpace, this); d->valueWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); d->valueWidget->setHandlePaintingStrategy(new KisClickableGLImageWidget::CircularHandleStrategy); connect(d->valueWidget, SIGNAL(selected(const QPointF&)), SLOT(slotValueSliderChanged(const QPointF&))); d->hasHardwareHDR = KisOpenGLModeProber::instance()->useHDRMode(); if (d->hasHardwareHDR) { d->dynamicRange = new KisSliderSpinBox(this); d->dynamicRange->setRange(80, 10000); d->dynamicRange->setExponentRatio(3.0); d->dynamicRange->setSingleStep(1); d->dynamicRange->setPageStep(100); d->dynamicRange->setSuffix("cd/m²"); d->dynamicRange->setValue(80.0 * d->currentRelativeDynamicRange); connect(d->dynamicRange, SIGNAL(valueChanged(int)), SLOT(slotInitiateUpdateDynamicRange(int))); } QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(d->hueWidget, 0); layout->addWidget(d->valueWidget, 1); if (d->dynamicRange) { layout->addSpacing(16); layout->addWidget(d->dynamicRange, 0); } setLayout(layout); slotUpdatePalettes(); } KisSmallColorWidget::~KisSmallColorWidget() { delete d; } void KisSmallColorWidget::setHue(qreal h) { h = qBound(0.0, h, 1.0); d->hue = h; d->colorChangedSignalCompressor->start(); d->valueSliderUpdateCompressor->start(); d->repaintCompressor->start(); } void KisSmallColorWidget::setHSV(qreal h, qreal s, qreal v, bool notifyChanged) { h = qBound(0.0, h, 1.0); s = qBound(0.0, s, 1.0); v = qBound(0.0, v, 1.0); bool newH = !qFuzzyCompare(d->hue, h); d->hue = h; d->value = v; d->saturation = s; // TODO: remove and make acyclic! if (notifyChanged) { d->colorChangedSignalCompressor->start(); } if(newH) { d->valueSliderUpdateCompressor->start(); } d->repaintCompressor->start(); } void KisSmallColorWidget::setColor(const KoColor &color) { if (!d->updateAllowed) return; KIS_SAFE_ASSERT_RECOVER(!d->dynamicRange || d->hasHDR == d->dynamicRange->isEnabled()) { slotDisplayConfigurationChanged(); } KIS_SAFE_ASSERT_RECOVER_RETURN(!d->hasHDR || d->hasHardwareHDR); const KoColorSpace *cs = d->generationColorSpace(); KIS_SAFE_ASSERT_RECOVER_RETURN(cs); KoColor newColor(color); newColor.convertTo(cs); QVector channels(4); cs->normalisedChannelsValue(newColor.data(), channels); float r, g, b; if (cs->colorDepthId() == Integer8BitsColorDepthID) { r = channels[2]; g = channels[1]; b = channels[0]; } else { r = channels[0]; g = channels[1]; b = channels[2]; } if (d->hasHDR) { qreal rangeCoeff = d->effectiveRelativeDynamicRange(); if (rangeCoeff < r || rangeCoeff < g || rangeCoeff < b) { rangeCoeff = std::max({r, g, b}) * 1.10f; const int newMaxLuminance = qRound(80.0 * rangeCoeff); updateDynamicRange(newMaxLuminance); d->dynamicRange->setValue(newMaxLuminance); } r /= rangeCoeff; g /= rangeCoeff; b /= rangeCoeff; } else { r = qBound(0.0f, r, 1.0f); g = qBound(0.0f, g, 1.0f); b = qBound(0.0f, b, 1.0f); } float denormHue, saturation, value; RGBToHSV(r, g, b, &denormHue, &saturation, &value); d->hueWidget->setNormalizedPos(QPointF(denormHue / 360.0, 0.0)); d->valueWidget->setNormalizedPos(QPointF(saturation, 1.0 - value)); setHSV(denormHue / 360.0, saturation, value, false); } void KisSmallColorWidget::slotUpdatePalettes() { updateHuePalette(); updateSVPalette(); } namespace { struct FillHPolicy { static inline void getRGB(qreal /*hue*/, float xPortionCoeff, float /*yPortionCoeff*/, int x, int /*y*/, float *r, float *g, float *b) { HSVToRGB(xPortionCoeff * x * 360.0f, 1.0, 1.0, r, g, b); } }; struct FillSVPolicy { static inline void getRGB(qreal hue, float xPortionCoeff, float yPortionCoeff, int x, int y, float *r, float *g, float *b) { HSVToRGB(hue * 360.0, xPortionCoeff * x, 1.0 - yPortionCoeff * y, r, g, b); } }; } template void KisSmallColorWidget::uploadPaletteData(KisGLImageWidget *widget, const QSize &size) { if (size.isEmpty()) return; KisGLImageF16 image(size); const float xPortionCoeff = 1.0 / image.width(); const float yPortionCoeff = 1.0 / image.height(); const float rangeCoeff = d->effectiveRelativeDynamicRange(); const KoColorSpace *generationColorSpace = d->generationColorSpace(); if (d->displayColorConverter->canSkipDisplayConversion(generationColorSpace)) { half *pixelPtr = image.data(); for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { Imf::Rgba &pxl = reinterpret_cast(*pixelPtr); float r, g, b; FillPolicy::getRGB(d->hue, xPortionCoeff, yPortionCoeff, x, y, &r, &g, &b); pxl.r = r * rangeCoeff; pxl.g = g * rangeCoeff; pxl.b = b * rangeCoeff; pxl.a = 1.0; pixelPtr += 4; } } } else { KIS_SAFE_ASSERT_RECOVER_RETURN(d->displayColorConverter); KisFixedPaintDeviceSP device = new KisFixedPaintDevice(generationColorSpace); device->setRect(QRect(QPoint(), image.size())); device->reallocateBufferWithoutInitialization(); float *devicePtr = reinterpret_cast(device->data()); for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { FillPolicy::getRGB(d->hue, xPortionCoeff, yPortionCoeff, x, y, devicePtr, devicePtr + 1, devicePtr + 2); devicePtr[0] *= rangeCoeff; devicePtr[1] *= rangeCoeff; devicePtr[2] *= rangeCoeff; devicePtr[3] = 1.0; devicePtr += 4; } } d->displayColorConverter->applyDisplayFilteringF32(device, Float32BitsColorDepthID); half *imagePtr = image.data(); devicePtr = reinterpret_cast(device->data()); for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { imagePtr[0] = devicePtr[0]; imagePtr[1] = devicePtr[1]; imagePtr[2] = devicePtr[2]; imagePtr[3] = devicePtr[3]; devicePtr += 4; imagePtr += 4; } } } widget->loadImage(image); } void KisSmallColorWidget::updateHuePalette() { uploadPaletteData(d->hueWidget, QSize(d->hueWidget->width(), d->huePreferredHeight)); } void KisSmallColorWidget::updateSVPalette() { const int maxSize = 256; QSize newSize = d->valueWidget->size(); newSize.rwidth() = qMin(maxSize, newSize.width()); newSize.rheight() = qMin(maxSize, newSize.height()); uploadPaletteData(d->valueWidget, newSize); } void KisSmallColorWidget::slotHueSliderChanged(const QPointF &pos) { const qreal newHue = pos.x(); if (!qFuzzyCompare(newHue, d->hue)) { setHue(newHue); } } void KisSmallColorWidget::slotValueSliderChanged(const QPointF &pos) { const qreal newSaturation = pos.x(); const qreal newValue = 1.0 - pos.y(); if (!qFuzzyCompare(newSaturation, d->saturation) || !qFuzzyCompare(newValue, d->value)) { setHSV(d->hue, newSaturation, newValue); } } void KisSmallColorWidget::slotInitiateUpdateDynamicRange(int maxLuminance) { d->dynamicRangeCompressor->start(maxLuminance); } void KisSmallColorWidget::updateDynamicRange(int maxLuminance) { const qreal oldRange = d->currentRelativeDynamicRange; const qreal newRange = qreal(maxLuminance) / 80.0; if (qFuzzyCompare(oldRange, newRange)) return; float r, g, b; float denormHue = d->hue * 360.0; float saturation = d->saturation; float value = d->value; HSVToRGB(denormHue, saturation, value, &r, &g, &b); const qreal transformCoeff = oldRange / newRange; r = qBound(0.0, r * transformCoeff, 1.0); g = qBound(0.0, g * transformCoeff, 1.0); b = qBound(0.0, b * transformCoeff, 1.0); RGBToHSV(r, g, b, &denormHue, &saturation, &value); d->currentRelativeDynamicRange = newRange; slotUpdatePalettes(); setHSV(denormHue / 360.0, saturation, value, false); d->hueWidget->setNormalizedPos(QPointF(denormHue / 360.0, 0)); d->valueWidget->setNormalizedPos(QPointF(saturation, 1.0 - value)); } void KisSmallColorWidget::setDisplayColorConverter(KisDisplayColorConverter *converter) { d->colorConverterConnections.clear(); if (!converter) { converter = KisDisplayColorConverter::dumbConverterInstance(); } d->displayColorConverter = converter; if (d->displayColorConverter) { d->colorConverterConnections.addConnection( d->displayColorConverter, SIGNAL(displayConfigurationChanged()), this, SLOT(slotDisplayConfigurationChanged())); } slotDisplayConfigurationChanged(); } void KisSmallColorWidget::slotDisplayConfigurationChanged() { d->hasHDR = false; if (d->hasHardwareHDR) { const KoColorSpace *cs = d->displayColorConverter->paintingColorSpace(); d->hasHDR = cs->colorModelId() == RGBAColorModelID && (cs->colorDepthId() == Float16BitsColorDepthID || cs->colorDepthId() == Float32BitsColorDepthID || cs->colorDepthId() == Float64BitsColorDepthID || cs->profile()->uniqueId() == KoColorSpaceRegistry::instance()->p2020PQProfile()->uniqueId()); } if (d->dynamicRange) { d->dynamicRange->setEnabled(d->hasHDR); } d->hueWidget->setUseHandleOpacity(!d->hasHDR); d->valueWidget->setUseHandleOpacity(!d->hasHDR); slotUpdatePalettes(); // TODO: also set the currently selected color again } void KisSmallColorWidget::slotTellColorChanged() { d->updateAllowed = false; float r, g, b; HSVToRGB(d->hue * 360.0, d->saturation, d->value, &r, &g, &b); if (d->hasHDR) { const float rangeCoeff = d->effectiveRelativeDynamicRange(); r *= rangeCoeff; g *= rangeCoeff; b *= rangeCoeff; } const KoColorSpace *cs = d->generationColorSpace(); KIS_SAFE_ASSERT_RECOVER_RETURN(cs); QVector values(4); if (cs->colorDepthId() == Integer8BitsColorDepthID) { values[0] = b; values[1] = g; values[2] = r; values[3] = 1.0f; } else { values[0] = r; values[1] = g; values[2] = b; values[3] = 1.0f; } KoColor c(cs); cs->fromNormalisedChannelsValue(c.data(), values); emit colorChanged(c); d->updateAllowed = true; } void KisSmallColorWidget::resizeEvent(QResizeEvent * event) { QWidget::resizeEvent(event); update(); d->resizeUpdateCompressor->start(); } diff --git a/plugins/dockers/smallcolorselector/kis_small_color_widget.h b/plugins/dockers/smallcolorselector/kis_small_color_widget.h index e771d053af..40af9a5624 100644 --- a/plugins/dockers/smallcolorselector/kis_small_color_widget.h +++ b/plugins/dockers/smallcolorselector/kis_small_color_widget.h @@ -1,76 +1,77 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_SMALL_COLOR_WIDGET_H_ #define _KIS_SMALL_COLOR_WIDGET_H_ #include class KoColor; class KisDisplayColorConverter; class KisGLImageWidget; class KisSmallColorWidget : public QWidget { Q_OBJECT public: KisSmallColorWidget(QWidget* parent); ~KisSmallColorWidget() override; public: void resizeEvent(QResizeEvent * event) override; void setDisplayColorConverter(KisDisplayColorConverter *converter); public: public Q_SLOTS: void setHue(qreal h); void setHSV(qreal h, qreal s, qreal v, bool notifyChanged = true); void setColor(const KoColor &color); void slotUpdatePalettes(); void updateSVPalette(); Q_SIGNALS: void colorChanged(const KoColor&); void sigTellColorChangedInternal(); private Q_SLOTS: void slotHueSliderChanged(const QPointF &pos); void slotValueSliderChanged(const QPointF &pos); void slotInitiateUpdateDynamicRange(int maxLuminance); void slotDisplayConfigurationChanged(); void slotTellColorChanged(); private: void updateDynamicRange(int maxLuminance); private: void updateHuePalette(); template void uploadPaletteData(KisGLImageWidget *widget, const QSize &size); private: struct Private; Private* const d; }; #endif diff --git a/plugins/dockers/smallcolorselector/smallcolorselector.cc b/plugins/dockers/smallcolorselector/smallcolorselector.cc index b90d5ac00a..308bf0ad8b 100644 --- a/plugins/dockers/smallcolorselector/smallcolorselector.cc +++ b/plugins/dockers/smallcolorselector/smallcolorselector.cc @@ -1,68 +1,69 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "smallcolorselector.h" #include #include #include #include "smallcolorselector_dock.h" K_PLUGIN_FACTORY_WITH_JSON(SmallColorSelectorPluginFactory, "krita_smallcolorselector.json", registerPlugin();) class SmallColorSelectorDockFactory : public KoDockFactoryBase { public: SmallColorSelectorDockFactory() { } QString id() const override { return QString("SmallColorSelector"); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { SmallColorSelectorDock * dockWidget = new SmallColorSelectorDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; SmallColorSelectorPlugin::SmallColorSelectorPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new SmallColorSelectorDockFactory()); } SmallColorSelectorPlugin::~SmallColorSelectorPlugin() { } #include "smallcolorselector.moc" diff --git a/plugins/dockers/smallcolorselector/smallcolorselector.h b/plugins/dockers/smallcolorselector/smallcolorselector.h index 8c78bfb170..fb8752b776 100644 --- a/plugins/dockers/smallcolorselector/smallcolorselector.h +++ b/plugins/dockers/smallcolorselector/smallcolorselector.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SMALLCOLORSELECTOR_H_ #define _SMALLCOLORSELECTOR_H_ #include #include /** * Template of view plugin */ class SmallColorSelectorPlugin : public QObject { Q_OBJECT public: SmallColorSelectorPlugin(QObject *parent, const QVariantList &); ~SmallColorSelectorPlugin() override; }; #endif diff --git a/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc b/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc index 50fb2ca903..dc4279392c 100644 --- a/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc +++ b/plugins/dockers/smallcolorselector/smallcolorselector_dock.cc @@ -1,84 +1,85 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "smallcolorselector_dock.h" #include #include "kis_canvas2.h" #include "kis_small_color_widget.h" #include "kis_canvas_resource_provider.h" #include #include SmallColorSelectorDock::SmallColorSelectorDock() : QDockWidget() , m_canvas(0) { QWidget *page = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(page); m_smallColorWidget = new KisSmallColorWidget(this); layout->addWidget(m_smallColorWidget, 1); page->setLayout(layout); setWidget(page); m_smallColorWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); connect(m_smallColorWidget, SIGNAL(colorChanged(KoColor)), this, SLOT(colorChangedProxy(KoColor))); connect(this, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), m_smallColorWidget, SLOT(update())); setWindowTitle(i18n("Small Color Selector")); } void SmallColorSelectorDock::setCanvas(KoCanvasBase * canvas) { setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); m_smallColorWidget->setColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); m_smallColorWidget->setDisplayColorConverter(0); } m_canvas = canvas; if (m_canvas && m_canvas->resourceManager()) { connect(m_canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(canvasResourceChanged(int,QVariant))); KisCanvas2 *kisCanvas = dynamic_cast(canvas); m_smallColorWidget->setDisplayColorConverter(kisCanvas->displayColorConverter()); m_smallColorWidget->setColor(m_canvas->resourceManager()->foregroundColor()); } } void SmallColorSelectorDock::colorChangedProxy(const KoColor& c) { if (m_canvas) m_canvas->resourceManager()->setForegroundColor(c); } void SmallColorSelectorDock::canvasResourceChanged(int key, const QVariant& v) { if (key == KoCanvasResourceProvider::ForegroundColor) { m_smallColorWidget->setColor(v.value()); } } diff --git a/plugins/dockers/smallcolorselector/smallcolorselector_dock.h b/plugins/dockers/smallcolorselector/smallcolorselector_dock.h index c3c36a747d..b25b0601b2 100644 --- a/plugins/dockers/smallcolorselector/smallcolorselector_dock.h +++ b/plugins/dockers/smallcolorselector/smallcolorselector_dock.h @@ -1,48 +1,49 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SMALLCOLORSELECTOR_DOCK_H_ #define _SMALLCOLORSELECTOR_DOCK_H_ #include #include #include #include class KoColor; class KisSmallColorWidget; class SmallColorSelectorDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: SmallColorSelectorDock(); QString observerName() override { return "SmallColorSelectorDock"; } /// reimplemented from KoCanvasObserverBase void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override { m_canvas = 0; setEnabled(false); } public Q_SLOTS: void colorChangedProxy(const KoColor &); void canvasResourceChanged(int, const QVariant&); private: KisSmallColorWidget* m_smallColorWidget; QPointer m_canvas; }; #endif diff --git a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc index 4aacf59369..94f5cab633 100644 --- a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc +++ b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.cc @@ -1,269 +1,270 @@ /* * Copyright (c) 2008 Cyrille Berger * Copyright (c) 2015 Moritz Molch * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_specific_color_selector_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_wdgSpecificColorSelectorWidget.h" KisSpecificColorSelectorWidget::KisSpecificColorSelectorWidget(QWidget* parent) : QWidget(parent) , m_colorSpace(0) , m_spacer(0) , m_updateCompressor(new KisSignalCompressor(10, KisSignalCompressor::POSTPONE, this)) , m_customColorSpaceSelected(false) , m_displayConverter(0) { m_ui = new Ui_wdgSpecificColorSelectorWidget(); m_ui->setupUi(this); m_updateAllowed = true; connect(m_updateCompressor, SIGNAL(timeout()), SLOT(updateTimeout())); m_colorspaceSelector = new KisColorSpaceSelector(this); connect(m_colorspaceSelector, SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(setCustomColorSpace(const KoColorSpace*))); m_ui->colorspacePopupButton->setPopupWidget(m_colorspaceSelector); connect(m_ui->chkUsePercentage, SIGNAL(toggled(bool)), this, SLOT(onChkUsePercentageChanged(bool))); KConfigGroup cfg = KSharedConfig::openConfig()->group(QString()); m_ui->chkUsePercentage->setChecked(cfg.readEntry("SpecificColorSelector/UsePercentage", false)); m_ui->chkUsePercentage->setIcon(KisIconUtils::loadIcon("ratio")); m_colorspaceSelector->showColorBrowserButton(false); m_spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); m_ui->slidersLayout->addItem(m_spacer); } KisSpecificColorSelectorWidget::~KisSpecificColorSelectorWidget() { KConfigGroup cfg = KSharedConfig::openConfig()->group(QString()); cfg.writeEntry("SpecificColorSelector/UsePercentage", m_ui->chkUsePercentage->isChecked()); } bool KisSpecificColorSelectorWidget::customColorSpaceUsed() { return m_customColorSpaceSelected; } void KisSpecificColorSelectorWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); if (m_colorSpace) { QString elidedColorspaceName = m_ui->colorspacePopupButton->fontMetrics().elidedText( m_colorSpace->name(), Qt::ElideRight, m_ui->colorspacePopupButton->width() ); m_ui->colorspacePopupButton->setText(elidedColorspaceName); } } void KisSpecificColorSelectorWidget::setDisplayConverter(KisDisplayColorConverter *displayConverter) { const bool needsForceUpdate = m_displayConverter != displayConverter; m_displayConverter = displayConverter; if (m_displayConverter) { m_converterConnection.clear(); m_converterConnection.addConnection(m_displayConverter, SIGNAL(displayConfigurationChanged()), this, SLOT(rereadCurrentColorSpace()), Qt::UniqueConnection); } rereadCurrentColorSpace(needsForceUpdate); } void KisSpecificColorSelectorWidget::rereadCurrentColorSpace(bool force) { if (m_displayConverter && !m_customColorSpaceSelected) { m_colorSpace = m_displayConverter->paintingColorSpace(); } setColorSpace(m_colorSpace, force); setColor(m_color); } void KisSpecificColorSelectorWidget::setColorSpace(const KoColorSpace* cs, bool force) { Q_ASSERT(cs); dbgPlugins << cs->id() << " " << cs->profile()->name(); if (*m_colorSpace == *cs && !force) { Q_FOREACH (KisColorInput* input, m_inputs) { input->update(); } return; } if (cs->colorDepthId() == Integer8BitsColorDepthID || cs->colorDepthId() == Integer16BitsColorDepthID) { m_ui->chkUsePercentage->setVisible(true); } else { m_ui->chkUsePercentage->setVisible(false); } m_colorSpace = KoColorSpaceRegistry::instance()->colorSpace(cs->colorModelId().id(), cs->colorDepthId().id(), cs->profile()); Q_ASSERT(m_colorSpace); Q_ASSERT(*m_colorSpace == *cs); QString elidedColorspaceName = m_ui->colorspacePopupButton->fontMetrics().elidedText( m_colorSpace->name(), Qt::ElideRight, m_ui->colorspacePopupButton->width() ); m_ui->colorspacePopupButton->setText(elidedColorspaceName); m_color = KoColor(m_color, m_colorSpace); Q_FOREACH (KisColorInput* input, m_inputs) { delete input; } m_inputs.clear(); m_ui->slidersLayout->removeItem(m_spacer); QList channels = KoChannelInfo::displayOrderSorted(m_colorSpace->channels()); KoColorDisplayRendererInterface *displayRenderer = m_displayConverter ? m_displayConverter->displayRendererInterface() : KisDisplayColorConverter::dumbConverterInstance()->displayRendererInterface(); Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR) { KisColorInput* input = 0; switch (channel->channelValueType()) { case KoChannelInfo::UINT8: case KoChannelInfo::UINT16: case KoChannelInfo::UINT32: { input = new KisIntegerColorInput(this, channel, &m_color, displayRenderer, m_ui->chkUsePercentage->isChecked()); } break; case KoChannelInfo::FLOAT16: case KoChannelInfo::FLOAT32: { input = new KisFloatColorInput(this, channel, &m_color, displayRenderer); } break; default: Q_ASSERT(false); input = 0; } if (input) { connect(input, SIGNAL(updated()), this, SLOT(update())); connect(this, SIGNAL(updated()), input, SLOT(update())); m_inputs.append(input); m_ui->slidersLayout->addWidget(input); } } } QList labels; int labelWidth = 0; Q_FOREACH (KisColorInput* input, m_inputs) { Q_FOREACH (QLabel* label, input->findChildren()) { labels.append(label); labelWidth = qMax(labelWidth, label->sizeHint().width()); } } Q_FOREACH (QLabel *label, labels) { label->setMinimumWidth(labelWidth); } bool allChannels8Bit = true; Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->channelType() == KoChannelInfo::COLOR && channel->channelValueType() != KoChannelInfo::UINT8) { allChannels8Bit = false; } } if (allChannels8Bit) { KisColorInput* input = new KisHexColorInput(this, &m_color, displayRenderer); m_inputs.append(input); m_ui->slidersLayout->addWidget(input); connect(input, SIGNAL(updated()), this, SLOT(update())); connect(this, SIGNAL(updated()), input, SLOT(update())); } m_ui->slidersLayout->addItem(m_spacer); m_colorspaceSelector->blockSignals(true); m_colorspaceSelector->setCurrentColorSpace(cs); m_colorspaceSelector->blockSignals(false); m_updateAllowed = false; emit(updated()); m_updateAllowed = true; } void KisSpecificColorSelectorWidget::update() { if (m_updateAllowed) { m_updateCompressor->start(); } } void KisSpecificColorSelectorWidget::setColor(const KoColor& c) { m_updateAllowed = false; m_color.fromKoColor(c); emit(updated()); m_updateAllowed = true; } void KisSpecificColorSelectorWidget::updateTimeout() { emit(colorChanged(m_color)); } void KisSpecificColorSelectorWidget::setCustomColorSpace(const KoColorSpace *colorSpace) { m_customColorSpaceSelected = true; setColorSpace(colorSpace); setColor(m_color); } void KisSpecificColorSelectorWidget::onChkUsePercentageChanged(bool isChecked) { for (auto input: m_inputs) { input->setPercentageWise(isChecked); } emit(updated()); } diff --git a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.h b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.h index 2b2b3c9340..272a3c54c1 100644 --- a/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.h +++ b/plugins/dockers/specificcolorselector/kis_specific_color_selector_widget.h @@ -1,80 +1,81 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_SPECIFIC_COLOR_SELECTOR_WIDGET_H_ #define _KIS_SPECIFIC_COLOR_SELECTOR_WIDGET_H_ #include #include #include "kis_signal_auto_connection.h" #include "ui_wdgSpecificColorSelectorWidget.h" class KoColorSpace; class QVBoxLayout; class KisColorInput; class KisColorSpaceSelector; class QCheckBox; class KisSignalCompressor; class QSpacerItem; class KisDisplayColorConverter; class KisPopupButton; class KisSpecificColorSelectorWidget : public QWidget { Q_OBJECT public: KisSpecificColorSelectorWidget(QWidget* parent); ~KisSpecificColorSelectorWidget() override; bool customColorSpaceUsed(); protected: void resizeEvent(QResizeEvent* event) override; public Q_SLOTS: void setDisplayConverter(KisDisplayColorConverter *colorConverter); void setColorSpace(const KoColorSpace *cs, bool force = false); void setColor(const KoColor&); private Q_SLOTS: void update(); void updateTimeout(); void setCustomColorSpace(const KoColorSpace *); void rereadCurrentColorSpace(bool force = false); void onChkUsePercentageChanged(bool); Q_SIGNALS: void colorChanged(const KoColor&); void updated(); private: QList m_inputs; const KoColorSpace* m_colorSpace; QSpacerItem *m_spacer; KoColor m_color; bool m_updateAllowed; KisSignalCompressor *m_updateCompressor; KisColorSpaceSelector *m_colorspaceSelector; bool m_customColorSpaceSelected; Ui_wdgSpecificColorSelectorWidget* m_ui; KisDisplayColorConverter *m_displayConverter; KisSignalAutoConnectionsStore m_converterConnection; }; #endif diff --git a/plugins/dockers/specificcolorselector/specificcolorselector.cc b/plugins/dockers/specificcolorselector/specificcolorselector.cc index 4a70c35028..2b163da342 100644 --- a/plugins/dockers/specificcolorselector/specificcolorselector.cc +++ b/plugins/dockers/specificcolorselector/specificcolorselector.cc @@ -1,81 +1,82 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "specificcolorselector.h" #include #include #include #include #include #include #include #include "kis_config.h" #include "kis_cursor.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "specificcolorselector_dock.h" K_PLUGIN_FACTORY_WITH_JSON(SpecificColorSelectorPluginFactory, "krita_specificcolorselector.json", registerPlugin();) class SpecificColorSelectorDockFactory : public KoDockFactoryBase { public: SpecificColorSelectorDockFactory() { } QString id() const override { return QString("SpecificColorSelector"); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { SpecificColorSelectorDock * dockWidget = new SpecificColorSelectorDock(); dockWidget->setObjectName(id()); return dockWidget; } KoDockFactoryBase::DockPosition defaultDockPosition() const override { return DockMinimized; } }; SpecificColorSelectorPlugin::SpecificColorSelectorPlugin(QObject *parent, const QVariantList &) : QObject(parent) { dbgPlugins << "SpecificColorSelectorPlugin"; KoDockRegistry::instance()->add(new SpecificColorSelectorDockFactory()); } SpecificColorSelectorPlugin::~SpecificColorSelectorPlugin() { } #include "specificcolorselector.moc" diff --git a/plugins/dockers/specificcolorselector/specificcolorselector.h b/plugins/dockers/specificcolorselector/specificcolorselector.h index 52bec70c0b..42b3671483 100644 --- a/plugins/dockers/specificcolorselector/specificcolorselector.h +++ b/plugins/dockers/specificcolorselector/specificcolorselector.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SPECIFICCOLORSELECTOR_H_ #define _SPECIFICCOLORSELECTOR_H_ #include #include /** * Template of view plugin */ class SpecificColorSelectorPlugin : public QObject { Q_OBJECT public: SpecificColorSelectorPlugin(QObject *parent, const QVariantList &); ~SpecificColorSelectorPlugin() override; }; #endif diff --git a/plugins/dockers/specificcolorselector/specificcolorselector_dock.cc b/plugins/dockers/specificcolorselector/specificcolorselector_dock.cc index cc51c97ab8..04d9dd6939 100644 --- a/plugins/dockers/specificcolorselector/specificcolorselector_dock.cc +++ b/plugins/dockers/specificcolorselector/specificcolorselector_dock.cc @@ -1,74 +1,75 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "specificcolorselector_dock.h" #include #include #include #include #include #include #include #include #include "kis_specific_color_selector_widget.h" SpecificColorSelectorDock::SpecificColorSelectorDock() : QDockWidget(i18n("Specific Color Selector")) , m_canvas(0) , m_view(0) , m_colorSelector(new KisSpecificColorSelectorWidget(this)) { setWidget(m_colorSelector); widget()->setContentsMargins(4,4,4,0); } void SpecificColorSelectorDock::setCanvas(KoCanvasBase * canvas) { setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); } KisCanvas2* kisCanvas = dynamic_cast(canvas); m_canvas = kisCanvas; if (!kisCanvas) { return; } m_colorSelector->setDisplayConverter(kisCanvas->displayColorConverter()); } void SpecificColorSelectorDock::unsetCanvas() { setEnabled(false); m_canvas = 0; m_colorSelector->setDisplayConverter(0); } void SpecificColorSelectorDock::setViewManager(KisViewManager* kisview) { m_view = kisview; connect(m_view->canvasResourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), m_colorSelector, SLOT(setColor(KoColor))); connect(m_colorSelector, SIGNAL(colorChanged(KoColor)), m_view->canvasResourceProvider(), SLOT(slotSetFGColor(KoColor))); } #include "moc_specificcolorselector_dock.cpp" diff --git a/plugins/dockers/specificcolorselector/specificcolorselector_dock.h b/plugins/dockers/specificcolorselector/specificcolorselector_dock.h index 619b3a3981..8ab881fe45 100644 --- a/plugins/dockers/specificcolorselector/specificcolorselector_dock.h +++ b/plugins/dockers/specificcolorselector/specificcolorselector_dock.h @@ -1,50 +1,51 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SPECIFICCOLORSELECTOR_DOCK_H_ #define _SPECIFICCOLORSELECTOR_DOCK_H_ #include #include #include #include #include class KisViewManager; class KisSpecificColorSelectorWidget; class SpecificColorSelectorDock : public QDockWidget, public KisMainwindowObserver { Q_OBJECT public: SpecificColorSelectorDock(); QString observerName() override { return "SpecificColorSelectorDock"; } /// reimplemented from KoCanvasObserverBase/KisMainwindowObserver void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; void setViewManager(KisViewManager* kisview) override; private: QPointer m_canvas; KisViewManager *m_view; KisSpecificColorSelectorWidget* m_colorSelector; }; #endif diff --git a/plugins/dockers/tasksetdocker/taskset_resource.cpp b/plugins/dockers/tasksetdocker/taskset_resource.cpp index f61a2835f3..ca6a81ede1 100644 --- a/plugins/dockers/tasksetdocker/taskset_resource.cpp +++ b/plugins/dockers/tasksetdocker/taskset_resource.cpp @@ -1,128 +1,129 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "taskset_resource.h" #include #include #include #include #include #include #define TASKSET_VERSION 1 TasksetResource::TasksetResource(const QString& f) : KoResource(f) { } TasksetResource::~TasksetResource() { } bool TasksetResource::save() { if (filename().isEmpty()) return false; QFile file(filename()); file.open(QIODevice::WriteOnly); bool res = saveToDevice(&file); file.close(); return res; } bool TasksetResource::load() { QString fn = filename(); if (fn.isEmpty()) return false; QFile file(fn); if (file.size() == 0) return false; if (!file.open(QIODevice::ReadOnly)) { warnKrita << "Can't open file " << filename(); return false; } bool res = loadFromDevice(&file); file.close(); return res; } bool TasksetResource::loadFromDevice(QIODevice *dev) { QDomDocument doc; if (!doc.setContent(dev)) { return false; } QDomElement element = doc.documentElement(); setName(element.attribute("name")); QDomNode node = element.firstChild(); while (!node.isNull()) { QDomElement child = node.toElement(); if (!child.isNull() && child.tagName() == "action") { m_actions.append(child.text()); } node = node.nextSibling(); } setValid(true); return true; } QString TasksetResource::defaultFileExtension() const { return QString(".kts"); } void TasksetResource::setActionList(const QStringList actions) { m_actions = actions; } QStringList TasksetResource::actionList() { return m_actions; } bool TasksetResource::saveToDevice(QIODevice *io) const { QDomDocument doc; QDomElement root = doc.createElement("Taskset"); root.setAttribute("name", name() ); root.setAttribute("version", TASKSET_VERSION); Q_FOREACH (const QString& action, m_actions) { QDomElement element = doc.createElement("action"); element.appendChild(doc.createTextNode(action)); root.appendChild(element); } doc.appendChild(root); QTextStream textStream(io); textStream.setCodec("UTF-8"); doc.save(textStream, 4); KoResource::saveToDevice(io); return true; } diff --git a/plugins/dockers/tasksetdocker/taskset_resource.h b/plugins/dockers/tasksetdocker/taskset_resource.h index 4018047dd8..4af0f44ba2 100644 --- a/plugins/dockers/tasksetdocker/taskset_resource.h +++ b/plugins/dockers/tasksetdocker/taskset_resource.h @@ -1,50 +1,51 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TASKSET_RESOURCE_H #define TASKSET_RESOURCE_H #include #include class TasksetResource : public KoResource { public: TasksetResource(const QString& filename); ~TasksetResource() override; bool load() override; bool loadFromDevice(QIODevice *dev) override; bool save() override; bool saveToDevice(QIODevice* dev) const override; QString defaultFileExtension() const override; void setActionList(const QStringList actions); QStringList actionList(); private: QStringList m_actions; }; typedef QSharedPointer TasksetResourceSP; #endif // TASKSET_RESOURCE_H diff --git a/plugins/dockers/tasksetdocker/tasksetdocker.cpp b/plugins/dockers/tasksetdocker/tasksetdocker.cpp index 667f8caf49..7d36f07813 100644 --- a/plugins/dockers/tasksetdocker/tasksetdocker.cpp +++ b/plugins/dockers/tasksetdocker/tasksetdocker.cpp @@ -1,77 +1,78 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tasksetdocker.h" #include #include #include #include #include "tasksetdocker_dock.h" K_PLUGIN_FACTORY_WITH_JSON(TasksetDockerPluginFactory, "krita_tasksetdocker.json", registerPlugin();) class TasksetDockerDockFactory : public KoDockFactoryBase { public: TasksetDockerDockFactory() { } QString id() const override { return QString( "TasksetDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { TasksetDockerDock * dockWidget = new TasksetDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } private: }; TasksetDockerPlugin::TasksetDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new TasksetDockerDockFactory()); } TasksetDockerPlugin::~TasksetDockerPlugin() { } #include "tasksetdocker.moc" diff --git a/plugins/dockers/tasksetdocker/tasksetdocker.h b/plugins/dockers/tasksetdocker/tasksetdocker.h index fbacf0eec4..57dd4893fa 100644 --- a/plugins/dockers/tasksetdocker/tasksetdocker.h +++ b/plugins/dockers/tasksetdocker/tasksetdocker.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TASKSETDOCKER_H #define TASKSETDOCKER_H #include #include /** * Docker showing the channels of the current layer */ class TasksetDockerPlugin : public QObject { Q_OBJECT public: TasksetDockerPlugin(QObject *parent, const QVariantList &); ~TasksetDockerPlugin() override; }; #endif diff --git a/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp b/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp index d291373b32..45665ed7a8 100644 --- a/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp +++ b/plugins/dockers/tasksetdocker/tasksetdocker_dock.cpp @@ -1,232 +1,233 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tasksetdocker_dock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tasksetmodel.h" class KisTasksetDelegate : public QStyledItemDelegate { public: KisTasksetDelegate(QObject * parent = 0) : QStyledItemDelegate(parent) {} ~KisTasksetDelegate() override {} /// reimplemented QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const override { return QSize(QStyledItemDelegate::sizeHint(option, index).width(), qMin(QStyledItemDelegate::sizeHint(option, index).width(), 25)); } }; class KisTasksetResourceDelegate : public QStyledItemDelegate { public: KisTasksetResourceDelegate(QObject * parent = 0) : QStyledItemDelegate(parent) {} ~KisTasksetResourceDelegate() override {} /// reimplemented void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; }; void KisTasksetResourceDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { if (! index.isValid()) return; QString name = index.data(Qt::UserRole + KisResourceModel::Name).toString(); if (option.state & QStyle::State_Selected) { painter->setPen(QPen(option.palette.highlight(), 2.0)); painter->fillRect(option.rect, option.palette.highlight()); painter->setBrush(option.palette.highlightedText()); } else { painter->setBrush(option.palette.text()); } painter->drawText(option.rect.x() + 5, option.rect.y() + painter->fontMetrics().ascent() + 5, name); } TasksetDockerDock::TasksetDockerDock( ) : QDockWidget(i18n("Task Sets")), m_canvas(0), m_blocked(false) { QWidget* widget = new QWidget(this); setupUi(widget); m_model = new TasksetModel(this); tasksetView->setModel(m_model); tasksetView->setItemDelegate(new KisTasksetDelegate(this)); recordButton->setIcon(KisIconUtils::loadIcon("media-record")); recordButton->setCheckable(true); clearButton->setIcon(KisIconUtils::loadIcon("edit-delete")); saveButton->setIcon(KisIconUtils::loadIcon("document-save")); saveButton->setEnabled(false); chooserButton->setIcon(KisIconUtils::loadIcon("edit-copy")); m_rserver = new KoResourceServer(ResourceType::TaskSets); KisResourceLoaderRegistry::instance()->add(new KisResourceLoader(ResourceType::TaskSets, ResourceType::TaskSets, i18n("Task sets"), QStringList() << "application/x-krita-taskset")); KisResourceItemChooser *itemChooser = new KisResourceItemChooser(ResourceType::TaskSets, false, this); itemChooser->setItemDelegate(new KisTasksetResourceDelegate(this)); itemChooser->setFixedSize(500, 250); itemChooser->setRowHeight(30); itemChooser->setColumnCount(1); itemChooser->showTaggingBar(true); chooserButton->setPopupWidget(itemChooser); connect(itemChooser, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(resourceSelected(KoResourceSP ))); setWidget(widget); connect( tasksetView, SIGNAL(clicked(QModelIndex)), this, SLOT(activated(QModelIndex)) ); connect( recordButton, SIGNAL(toggled(bool)), this, SLOT(recordClicked())); connect( clearButton, SIGNAL(clicked(bool)), this, SLOT(clearClicked())); connect( saveButton, SIGNAL(clicked(bool)), this, SLOT(saveClicked())); } TasksetDockerDock::~TasksetDockerDock() { delete m_rserver; } void TasksetDockerDock::setCanvas(KoCanvasBase * canvas) { if (m_canvas && m_canvas->viewManager()) { m_canvas->viewManager()->actionCollection()->disconnect(this); Q_FOREACH (KXMLGUIClient* client, m_canvas->viewManager()->mainWindow()->childClients()) { client->actionCollection()->disconnect(this); } } m_canvas = dynamic_cast(canvas); } void TasksetDockerDock::unsetCanvas() { m_canvas = 0; m_model->clear(); } void TasksetDockerDock::actionTriggered(QAction* action) { if(action && !action->objectName().isEmpty() && !m_blocked && recordButton->isChecked()) { m_model->addAction(action); saveButton->setEnabled(true); } } void TasksetDockerDock::activated(const QModelIndex& index) { QAction* action = m_model->actionFromIndex(index); m_blocked = true; action->trigger(); m_blocked = false; } void TasksetDockerDock::recordClicked() { if(m_canvas) { KisViewManager* view = m_canvas->viewManager(); connect(view->actionCollection(), SIGNAL(actionTriggered(QAction*)), this, SLOT(actionTriggered(QAction*)), Qt::UniqueConnection); Q_FOREACH (KXMLGUIClient* client, view->mainWindow()->childClients()) { connect(client->actionCollection(), SIGNAL(actionTriggered(QAction*)), this, SLOT(actionTriggered(QAction*)), Qt::UniqueConnection); } } } void TasksetDockerDock::saveClicked() { bool ok; QString name = QInputDialog::getText(this, i18n("Taskset Name"), i18n("Name:"), QLineEdit::Normal, QString(), &ok); if (!ok) { return; } TasksetResourceSP taskset(new TasksetResource(QString())); QStringList actionNames; Q_FOREACH (QAction* action, m_model->actions()) { actionNames.append(action->objectName()); } taskset->setActionList(actionNames); taskset->setValid(true); QString saveLocation = m_rserver->saveLocation(); bool newName = false; if(name.isEmpty()) { newName = true; name = i18n("Taskset"); } QFileInfo fileInfo(saveLocation + name + taskset->defaultFileExtension()); int i = 1; while (fileInfo.exists()) { fileInfo.setFile(saveLocation + name + QString("%1").arg(i) + taskset->defaultFileExtension()); i++; } taskset->setFilename(fileInfo.filePath()); if(newName) { name = i18n("Taskset %1", i); } taskset->setName(name); m_rserver->addResource(taskset); } void TasksetDockerDock::clearClicked() { saveButton->setEnabled(false); m_model->clear(); } void TasksetDockerDock::resourceSelected(KoResourceSP resource) { if(!m_canvas) { return; } m_model->clear(); saveButton->setEnabled(true); Q_FOREACH (const QString& actionName, resource.staticCast()->actionList()) { QAction* action = m_canvas->viewManager()->actionCollection()->action(actionName); if(action) { m_model->addAction(action); } } } diff --git a/plugins/dockers/tasksetdocker/tasksetdocker_dock.h b/plugins/dockers/tasksetdocker/tasksetdocker_dock.h index c746900d22..58d7a5d404 100644 --- a/plugins/dockers/tasksetdocker/tasksetdocker_dock.h +++ b/plugins/dockers/tasksetdocker/tasksetdocker_dock.h @@ -1,61 +1,62 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TASKSETDOCKER_DOCK_H #define TASKSETDOCKER_DOCK_H #include #include #include #include #include #include #include "taskset_resource.h" #include "ui_wdgtasksetdocker.h" class TasksetModel; class TasksetDockerDock : public QDockWidget, public KoCanvasObserverBase, public Ui_WdgTasksetDocker { Q_OBJECT public: TasksetDockerDock(); ~TasksetDockerDock() override; QString observerName() override { return "TasksetDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; private Q_SLOTS: void actionTriggered(QAction* action); void activated (const QModelIndex& index); void recordClicked(); void saveClicked(); void clearClicked(); void resourceSelected( KoResourceSP resource ); private: QPointer m_canvas; TasksetModel *m_model; bool m_blocked; KoResourceServer *m_rserver {0}; }; #endif diff --git a/plugins/dockers/tasksetdocker/tasksetmodel.cpp b/plugins/dockers/tasksetdocker/tasksetmodel.cpp index 26661e1301..a95f09fff5 100644 --- a/plugins/dockers/tasksetdocker/tasksetmodel.cpp +++ b/plugins/dockers/tasksetdocker/tasksetmodel.cpp @@ -1,102 +1,103 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tasksetmodel.h" #include #include #include TasksetModel::TasksetModel(QObject* parent): QAbstractTableModel(parent) { } TasksetModel::~TasksetModel() { } QVariant TasksetModel::data(const QModelIndex& index, int role) const { if (index.isValid()) { switch (role) { case Qt::DisplayRole: { return m_actions.at(index.row())->iconText(); } case Qt::DecorationRole: { const QIcon icon = m_actions.at(index.row())->icon(); if (icon.isNull()) { return KisIconUtils::loadIcon("tools-wizard"); } return icon; } } } return QVariant(); } QVariant TasksetModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return i18n("Task"); } int TasksetModel::rowCount(const QModelIndex& /*parent*/) const { return m_actions.count(); } int TasksetModel::columnCount(const QModelIndex& /*parent*/) const { return 1; } Qt::ItemFlags TasksetModel::flags(const QModelIndex& /*index*/) const { Qt::ItemFlags flags = /*Qt::ItemIsSelectable |*/ Qt::ItemIsEnabled; return flags; } void TasksetModel::addAction(QAction* action) { m_actions.append(action); beginResetModel(); endResetModel(); } QVector< QAction* > TasksetModel::actions() { return m_actions; } QAction* TasksetModel::actionFromIndex(const QModelIndex& index) { if(index.isValid()) { return m_actions.at(index.row()); } return 0; } void TasksetModel::clear() { m_actions.clear(); beginResetModel(); endResetModel(); } diff --git a/plugins/dockers/tasksetdocker/tasksetmodel.h b/plugins/dockers/tasksetdocker/tasksetmodel.h index 524d75ad28..c8ac1c2af1 100644 --- a/plugins/dockers/tasksetdocker/tasksetmodel.h +++ b/plugins/dockers/tasksetdocker/tasksetmodel.h @@ -1,49 +1,50 @@ /* * Copyright (c) 2011 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TASKSETMODEL_H #define TASKSETMODEL_H #include #include class QAction; class TasksetModel : public QAbstractTableModel { Q_OBJECT public: TasksetModel(QObject* parent = 0); ~TasksetModel() override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; void addAction(QAction* action); QAction* actionFromIndex(const QModelIndex& index); QVector actions(); public Q_SLOTS: void clear(); private: QVector m_actions; }; #endif // TASKSETMODEL_H diff --git a/plugins/dockers/throttle/Throttle.h b/plugins/dockers/throttle/Throttle.h index 84e7a0a9b9..64af3f6b62 100644 --- a/plugins/dockers/throttle/Throttle.h +++ b/plugins/dockers/throttle/Throttle.h @@ -1,61 +1,62 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef THROTTLE_H #define THROTTLE_H #include class KisCanvas2; class KisSignalCompressor; class ThreadManager : public QObject { Q_OBJECT Q_PROPERTY(int threadCount READ threadCount WRITE setThreadCount NOTIFY threadCountChanged) Q_PROPERTY(int maxThreadCount READ maxThreadCount) public: ThreadManager(QObject *parent = 0); ~ThreadManager() override; void setThreadCount(int threadCount); int threadCount() const; int maxThreadCount() const; private Q_SLOTS: void slotDoUpdateConfig(); Q_SIGNALS: void threadCountChanged(); private: int m_threadCount = 0; KisSignalCompressor *m_configUpdateCompressor; }; class Throttle : public QQuickWidget { Q_OBJECT public: Throttle(QWidget *parent); ~Throttle() override; private: ThreadManager *m_threadManager {0}; }; #endif diff --git a/plugins/dockers/throttle/ThrottlePlugin.h b/plugins/dockers/throttle/ThrottlePlugin.h index 3fb02c1868..2b3d0d16eb 100644 --- a/plugins/dockers/throttle/ThrottlePlugin.h +++ b/plugins/dockers/throttle/ThrottlePlugin.h @@ -1,49 +1,50 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef THROTTLEPLUGIN_H #define THROTTLEPLUGIN_H #include #include #include #include #include class Throttle; class BasicDocker : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT public: BasicDocker() : QDockWidget(i18n("CPU Throttle")) {} ~BasicDocker() override {} QString observerName() override { return "ThrottleDocker"; } void setCanvas(KoCanvasBase *) override {} void unsetCanvas() override {} }; class ThrottlePlugin : public QObject { Q_OBJECT public: ThrottlePlugin(QObject *parent, const QVariantList &); ~ThrottlePlugin() override; }; #endif diff --git a/plugins/dockers/touchdocker/TouchDockerDock.cpp b/plugins/dockers/touchdocker/TouchDockerDock.cpp index 281e087913..c81463d8a5 100644 --- a/plugins/dockers/touchdocker/TouchDockerDock.cpp +++ b/plugins/dockers/touchdocker/TouchDockerDock.cpp @@ -1,428 +1,429 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "TouchDockerDock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { bool shouldSetAcceptTouchEvents() { // See https://bugreports.qt.io/browse/QTBUG-66718 static QVersionNumber qtVersion = QVersionNumber::fromString(qVersion()); static bool retval = qtVersion > QVersionNumber(5, 9, 3) && qtVersion.normalized() != QVersionNumber(5, 10); return retval; } } // namespace class TouchDockerDock::Private { public: Private() { } TouchDockerDock *q; bool allowClose {true}; KisSketchView *sketchView {0}; QString currentSketchPage; KoDialog *openDialog {0}; KoDialog *saveAsDialog {0}; QMap buttonMapping; bool shiftOn {false}; bool ctrlOn {false}; bool altOn {false}; }; TouchDockerDock::TouchDockerDock() : QDockWidget(i18n("Touch Docker")) , d(new Private()) { QStringList defaultMapping = QStringList() << "decrease_opacity" << "increase_opacity" << "make_brush_color_lighter" << "make_brush_color_darker" << "decrease_brush_size" << "increase_brush_size" << "previous_preset" << "clear"; QStringList mapping = KisConfig(true).readEntry("touchdockermapping", defaultMapping.join(',')).split(','); for (int i = 0; i < 8; ++i) { if (i < mapping.size()) { d->buttonMapping[QString("button%1").arg(i + 1)] = mapping[i]; } else if (i < defaultMapping.size()) { d->buttonMapping[QString("button%1").arg(i + 1)] = defaultMapping[i]; } } m_quickWidget = new QQuickWidget(this); if (shouldSetAcceptTouchEvents()) { m_quickWidget->setAttribute(Qt::WA_AcceptTouchEvents); } setWidget(m_quickWidget); setEnabled(true); m_quickWidget->engine()->rootContext()->setContextProperty("mainWindow", this); m_quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); m_quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); m_quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); m_quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); Settings *settings = new Settings(this); DocumentManager::instance()->setSettingsManager(settings); m_quickWidget->engine()->rootContext()->setContextProperty("Settings", settings); Theme *theme = Theme::load(KSharedConfig::openConfig()->group("General").readEntry("theme", "default"), m_quickWidget->engine()); if (theme) { settings->setTheme(theme); } m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); m_quickWidget->setSource(QUrl("qrc:/touchstrip.qml")); } TouchDockerDock::~TouchDockerDock() { } bool TouchDockerDock::allowClose() const { return d->allowClose; } void TouchDockerDock::setAllowClose(bool allow) { d->allowClose = allow; } QString TouchDockerDock::currentSketchPage() const { return d->currentSketchPage; } void TouchDockerDock::setCurrentSketchPage(QString newPage) { d->currentSketchPage = newPage; emit currentSketchPageChanged(); } void TouchDockerDock::closeEvent(QCloseEvent* event) { if (!d->allowClose) { event->ignore(); emit closeRequested(); } else { event->accept(); } } void TouchDockerDock::slotButtonPressed(const QString &id) { if (id == "fileOpenButton") { showFileOpenDialog(); } else if (id == "fileSaveButton" && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->document()) { bool batchMode = m_canvas->viewManager()->document()->fileBatchMode(); m_canvas->viewManager()->document()->setFileBatchMode(true); m_canvas->viewManager()->document()->save(true, 0); m_canvas->viewManager()->document()->setFileBatchMode(batchMode); } else if (id == "fileSaveAsButton" && m_canvas && m_canvas->viewManager() && m_canvas->viewManager()->document()) { showFileSaveAsDialog(); } else { QAction *a = action(id); if (a) { if (a->isCheckable()) { a->toggle(); } else { a->trigger(); } } else if (id == "shift") { // set shift state for the next pointer event, somehow QKeyEvent event(d->shiftOn ? QEvent::KeyRelease : QEvent::KeyPress, 0, Qt::ShiftModifier); QApplication::sendEvent(KisPart::instance()->currentMainwindow(), &event); d->shiftOn = !d->shiftOn; } else if (id == "ctrl") { // set ctrl state for the next pointer event, somehow QKeyEvent event(d->ctrlOn ? QEvent::KeyRelease : QEvent::KeyPress, 0, Qt::ControlModifier); QApplication::sendEvent(KisPart::instance()->currentMainwindow(), &event); d->ctrlOn = !d->ctrlOn; } else if (id == "alt") { // set alt state for the next pointer event, somehow QKeyEvent event(d->altOn ? QEvent::KeyRelease : QEvent::KeyPress, 0, Qt::AltModifier); QApplication::sendEvent(KisPart::instance()->currentMainwindow(), &event); d->altOn = !d->altOn; } } } void TouchDockerDock::slotOpenImage(QString path) { if (d->openDialog) { d->openDialog->accept(); } KisPart::instance()->currentMainwindow()->openDocument(QUrl::fromLocalFile(path), KisMainWindow::None); } void TouchDockerDock::slotSaveAs(QString path, QString mime) { if (d->saveAsDialog) { d->saveAsDialog->accept(); } m_canvas->viewManager()->document()->saveAs(QUrl::fromLocalFile(path), mime.toLatin1(), true); m_canvas->viewManager()->document()->waitForSavingToComplete(); } void TouchDockerDock::hideFileOpenDialog() { if (d->openDialog) { d->openDialog->accept(); } } void TouchDockerDock::hideFileSaveAsDialog() { if (d->saveAsDialog) { d->saveAsDialog->accept(); } } QString TouchDockerDock::imageForButton(QString id) { if (d->buttonMapping.contains(id)) { id = d->buttonMapping[id]; } if (KisActionRegistry::instance()->hasAction(id)) { QString a = KisActionRegistry::instance()->getActionProperty(id, "icon"); if (!a.isEmpty()) { return "image://icon/" + a; } } return QString(); } QString TouchDockerDock::textForButton(QString id) { if (d->buttonMapping.contains(id)) { id = d->buttonMapping[id]; } if (KisActionRegistry::instance()->hasAction(id)) { QString a = KisActionRegistry::instance()->getActionProperty(id, "iconText"); if (a.isEmpty()) { a = KisActionRegistry::instance()->getActionProperty(id, "text"); } return a; } return id; } QAction *TouchDockerDock::action(QString id) const { if (m_canvas && m_canvas->viewManager()) { if (d->buttonMapping.contains(id)) { id = d->buttonMapping[id]; } return m_canvas->viewManager()->actionManager()->actionByName(id); } return 0; } void TouchDockerDock::showFileOpenDialog() { if (!d->openDialog) { d->openDialog = createDialog("qrc:/opendialog.qml"); } d->openDialog->exec(); } void TouchDockerDock::showFileSaveAsDialog() { if (!d->saveAsDialog) { d->saveAsDialog = createDialog("qrc:/saveasdialog.qml"); } d->saveAsDialog->exec(); } void TouchDockerDock::changeEvent(QEvent *event) { if (event->type() == QEvent::PaletteChange) { m_quickWidget->setSource(QUrl("qrc:/touchstrip.qml")); event->accept(); } else { event->ignore(); } } void TouchDockerDock::tabletEvent(QTabletEvent *event) { #ifdef Q_OS_WIN /** * On Windows (only in WinInk mode), unless we accept the tablet event, * OS will start windows gestures, like click+hold for right click. * It will block any mouse events generation. * * In our own (hacky) implementation, if we accept the event, we block * the gesture, but still generate a fake mouse event. */ event->accept(); #else QDockWidget::tabletEvent(event); #endif } KoDialog *TouchDockerDock::createDialog(const QString qml) { KoDialog *dlg = new KoDialog(this); dlg->setButtons(KoDialog::None); QQuickWidget *quickWidget = new QQuickWidget(this); if (shouldSetAcceptTouchEvents()) { quickWidget->setAttribute(Qt::WA_AcceptTouchEvents); } dlg->setMainWidget(quickWidget); setEnabled(true); quickWidget->engine()->rootContext()->setContextProperty("mainWindow", this); quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); quickWidget->engine()->addImportPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib/qml/"); quickWidget->engine()->addPluginPath(KoResourcePaths::getApplicationRoot() + "/lib64/qml/"); Settings *settings = new Settings(this); DocumentManager::instance()->setSettingsManager(settings); quickWidget->engine()->rootContext()->setContextProperty("Settings", settings); Theme *theme = Theme::load(KSharedConfig::openConfig()->group("General").readEntry("theme", "default"), quickWidget->engine()); settings->setTheme(theme); quickWidget->setSource(QUrl(qml)); quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); dlg->setMinimumSize(1280, 768); return dlg; } QObject *TouchDockerDock::sketchKisView() const { return d->sketchView; } void TouchDockerDock::setSketchKisView(QObject* newView) { if (d->sketchView) { d->sketchView->disconnect(this); } if (d->sketchView != newView) { d->sketchView = qobject_cast(newView); emit sketchKisViewChanged(); } } void TouchDockerDock::setCanvas(KoCanvasBase *canvas) { setEnabled(true); if (m_canvas == canvas) { return; } if (m_canvas) { m_canvas->disconnectCanvasObserver(this); } if (!canvas) { m_canvas = 0; return; } m_canvas = dynamic_cast(canvas); } void TouchDockerDock::unsetCanvas() { setEnabled(true); m_canvas = 0; } diff --git a/plugins/dockers/touchdocker/TouchDockerDock.h b/plugins/dockers/touchdocker/TouchDockerDock.h index 478bc0c530..058b1d265b 100644 --- a/plugins/dockers/touchdocker/TouchDockerDock.h +++ b/plugins/dockers/touchdocker/TouchDockerDock.h @@ -1,93 +1,94 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TOUCHDOCKER_DOCK_H #define TOUCHDOCKER_DOCK_H #include #include #include #include #include class KoDialog; class QQuickWidget; class TouchDockerDock : public QDockWidget, public KoCanvasObserverBase { Q_OBJECT Q_PROPERTY(bool allowClose READ allowClose WRITE setAllowClose) Q_PROPERTY(QString currentSketchPage READ currentSketchPage WRITE setCurrentSketchPage NOTIFY currentSketchPageChanged) Q_PROPERTY(QObject* sketchKisView READ sketchKisView WRITE setSketchKisView NOTIFY sketchKisViewChanged) public: TouchDockerDock(); ~TouchDockerDock() override; QString observerName() override { return "TouchDockerDock"; } void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; bool allowClose() const; void setAllowClose(bool allow); QString currentSketchPage() const; void setCurrentSketchPage(QString newPage); QObject *sketchKisView() const; void setSketchKisView(QObject *newView); virtual void closeEvent(QCloseEvent *event); Q_SIGNALS: void closeRequested(); void currentSketchPageChanged(); void sketchKisViewChanged(); public Q_SLOTS: void slotButtonPressed(const QString &id); void slotOpenImage(QString path); void slotSaveAs(QString path, QString mime); void hideFileOpenDialog(); void hideFileSaveAsDialog(); QString imageForButton(QString id); QString textForButton(QString id); QAction *action(QString id) const; private: void showFileOpenDialog(); void showFileSaveAsDialog(); void changeEvent(QEvent* event) override; void tabletEvent(QTabletEvent *event) override; KoDialog *createDialog(const QString qml); QPointer m_canvas; QQuickWidget *m_quickWidget {0}; class Private; const QScopedPointer d; }; #endif diff --git a/plugins/dockers/touchdocker/TouchDockerPlugin.cpp b/plugins/dockers/touchdocker/TouchDockerPlugin.cpp index ef16fc8ee5..97bdf0200b 100644 --- a/plugins/dockers/touchdocker/TouchDockerPlugin.cpp +++ b/plugins/dockers/touchdocker/TouchDockerPlugin.cpp @@ -1,77 +1,78 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "TouchDockerPlugin.h" #include #include #include #include #include "kis_config.h" #include "kis_types.h" #include "KisViewManager.h" #include "TouchDockerDock.h" #include K_PLUGIN_FACTORY_WITH_JSON(TouchDockerPluginFactory, "krita_touchdocker.json", registerPlugin();) class TouchDockerDockFactory : public KoDockFactoryBase { public: TouchDockerDockFactory() { } QString id() const override { return QString( "TouchDocker" ); } virtual Qt::DockWidgetArea defaultDockWidgetArea() const { return Qt::RightDockWidgetArea; } QDockWidget* createDockWidget() override { TouchDockerDock * dockWidget = new TouchDockerDock(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockMinimized; } }; TouchDockerPlugin::TouchDockerPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KoDockRegistry::instance()->add(new TouchDockerDockFactory()); } TouchDockerPlugin::~TouchDockerPlugin() { } #include "TouchDockerPlugin.moc" diff --git a/plugins/dockers/touchdocker/TouchDockerPlugin.h b/plugins/dockers/touchdocker/TouchDockerPlugin.h index fc120672dc..73c38764c7 100644 --- a/plugins/dockers/touchdocker/TouchDockerPlugin.h +++ b/plugins/dockers/touchdocker/TouchDockerPlugin.h @@ -1,32 +1,33 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TOUCHDOCKERPLUGIN_H #define TOUCHDOCKERPLUGIN_H #include #include class TouchDockerPlugin : public QObject { Q_OBJECT public: TouchDockerPlugin(QObject *parent, const QVariantList &); ~TouchDockerPlugin() override; }; #endif diff --git a/plugins/filters/dodgeburn/DodgeBurn.cpp b/plugins/filters/dodgeburn/DodgeBurn.cpp index 322f767ba4..f5526f255c 100644 --- a/plugins/filters/dodgeburn/DodgeBurn.cpp +++ b/plugins/filters/dodgeburn/DodgeBurn.cpp @@ -1,112 +1,113 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "DodgeBurn.h" #include #include #include #include "ui_DodgeBurnConfigurationBaseWidget.h" KisFilterDodgeBurn::KisFilterDodgeBurn(const QString& id, const QString& prefix, const QString& name ) : KisColorTransformationFilter(KoID(id, name), FiltersCategoryAdjustId, name), m_prefix(prefix) { setColorSpaceIndependence(FULLY_INDEPENDENT); setSupportsPainting(true); } KisConfigWidget * KisFilterDodgeBurn::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const { Q_UNUSED(dev); return new KisDodgeBurnConfigWidget(parent, id()); } KoColorTransformation* KisFilterDodgeBurn::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { QHash params; QString suffix = "Midtones"; if (config) { params["exposure"] = config->getDouble("exposure", 0.5); int type = config->getInt("type", KisFilterDodgeBurn::MIDTONES); switch(type) { case KisFilterDodgeBurn::HIGHLIGHTS: suffix = "Highlights"; break; case KisFilterDodgeBurn::SHADOWS: suffix = "Shadows"; break; default: break; } } return cs->createColorTransformation(m_prefix + suffix, params); } KisDodgeBurnConfigWidget::KisDodgeBurnConfigWidget(QWidget * parent, const QString& id) : KisConfigWidget(parent), m_id(id) { m_page = new Ui_DodgeBurnConfigurationBaseWidget(); m_page->setupUi(this); connect(m_page->radioButtonHighlights, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->radioButtonMidtones, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->radioButtonShadows, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->sliderExposure, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); } KisDodgeBurnConfigWidget::~KisDodgeBurnConfigWidget() { delete m_page; } KisPropertiesConfigurationSP KisDodgeBurnConfigWidget::configuration() const { KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(m_id, 0); int type = 0; if(m_page->radioButtonHighlights->isChecked()) { type = KisFilterDodgeBurn::HIGHLIGHTS; } else if(m_page->radioButtonShadows->isChecked()) { type = KisFilterDodgeBurn::SHADOWS; } else { type = KisFilterDodgeBurn::MIDTONES; } c->setProperty("type", type); c->setProperty("exposure", m_page->sliderExposure->value() / 100.0); return c; } void KisDodgeBurnConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { int type = config->getInt("type", KisFilterDodgeBurn::MIDTONES); switch(type) { case KisFilterDodgeBurn::HIGHLIGHTS: m_page->radioButtonHighlights->setChecked(true); break; case KisFilterDodgeBurn::SHADOWS: m_page->radioButtonShadows->setChecked(true); break; default: case KisFilterDodgeBurn::MIDTONES: m_page->radioButtonMidtones->setChecked(true); break; } m_page->sliderExposure->setValue(config->getDouble("exposure", 0.5) * 100); } diff --git a/plugins/filters/dodgeburn/DodgeBurn.h b/plugins/filters/dodgeburn/DodgeBurn.h index 06dc1fa757..b7123ca13c 100644 --- a/plugins/filters/dodgeburn/DodgeBurn.h +++ b/plugins/filters/dodgeburn/DodgeBurn.h @@ -1,58 +1,59 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DODGE_BURN_H #define DODGE_BURN_H #include "filter/kis_color_transformation_filter.h" #include "kis_config_widget.h" class KisFilterDodgeBurn : public KisColorTransformationFilter { public: enum Type { SHADOWS, MIDTONES, HIGHLIGHTS }; public: KisFilterDodgeBurn(const QString& id, const QString& prefix, const QString& name ); public: KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override; KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override; private: QString m_prefix; }; class Ui_DodgeBurnConfigurationBaseWidget; class KisDodgeBurnConfigWidget : public KisConfigWidget { public: KisDodgeBurnConfigWidget(QWidget * parent, const QString& id); ~KisDodgeBurnConfigWidget() override; KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; QString m_id; Ui_DodgeBurnConfigurationBaseWidget * m_page; }; #endif diff --git a/plugins/filters/dodgeburn/DodgeBurnPlugin.cpp b/plugins/filters/dodgeburn/DodgeBurnPlugin.cpp index 4f1dc7d695..942ad93fbe 100644 --- a/plugins/filters/dodgeburn/DodgeBurnPlugin.cpp +++ b/plugins/filters/dodgeburn/DodgeBurnPlugin.cpp @@ -1,38 +1,39 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "DodgeBurnPlugin.h" #include #include #include "DodgeBurn.h" K_PLUGIN_FACTORY_WITH_JSON(DodgeBurnPluginFactory, "kritadodgeburn.json", registerPlugin();) DodgeBurnPlugin::DodgeBurnPlugin(QObject *parent, const QVariantList &) { Q_UNUSED(parent); KisFilterRegistry::instance()->add(new KisFilterDodgeBurn("dodge", "Dodge", i18n("Dodge"))); KisFilterRegistry::instance()->add(new KisFilterDodgeBurn("burn", "Burn", i18n("Burn"))); } DodgeBurnPlugin::~DodgeBurnPlugin() { } #include "DodgeBurnPlugin.moc" diff --git a/plugins/filters/dodgeburn/DodgeBurnPlugin.h b/plugins/filters/dodgeburn/DodgeBurnPlugin.h index b039ae9de0..2379e37145 100644 --- a/plugins/filters/dodgeburn/DodgeBurnPlugin.h +++ b/plugins/filters/dodgeburn/DodgeBurnPlugin.h @@ -1,32 +1,33 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _DODGE_BURN_PLUGIN_H_ #define _DODGE_BURN_PLUGIN_H_ #include #include class DodgeBurnPlugin : public QObject { Q_OBJECT public: DodgeBurnPlugin(QObject *parent, const QVariantList &); ~DodgeBurnPlugin() override; }; #endif diff --git a/plugins/filters/palettize/CMakeLists.txt b/plugins/filters/palettize/CMakeLists.txt index 1573abce5b..3aa4ae992d 100644 --- a/plugins/filters/palettize/CMakeLists.txt +++ b/plugins/filters/palettize/CMakeLists.txt @@ -1,4 +1,5 @@ set(kritapalettize_SOURCES palettize.cpp) +ki18n_wrap_ui(kritapalettize_SOURCES palettize.ui) add_library(kritapalettize MODULE ${kritapalettize_SOURCES}) target_link_libraries(kritapalettize kritaui) install(TARGETS kritapalettize DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/filters/palettize/palettize.cpp b/plugins/filters/palettize/palettize.cpp index f092f81291..9d85b8de05 100644 --- a/plugins/filters/palettize/palettize.cpp +++ b/plugins/filters/palettize/palettize.cpp @@ -1,277 +1,346 @@ /* * This file is part of Krita * * Copyright (c) 2019 Carl Olsson * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "palettize.h" -#include -#include #include -#include -#include -#include -#include -#include -#include #include +#include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include #include -#include -#include -#include #include +#include +#include +#include K_PLUGIN_FACTORY_WITH_JSON(PalettizeFactory, "kritapalettize.json", registerPlugin();) Palettize::Palettize(QObject *parent, const QVariantList &) : QObject(parent) { KisFilterRegistry::instance()->add(new KisFilterPalettize()); } +#include "palettize.moc" + KisFilterPalettize::KisFilterPalettize() : KisFilter(id(), FiltersCategoryMapId, i18n("&Palettize...")) { setColorSpaceIndependence(FULLY_INDEPENDENT); setSupportsPainting(true); setShowConfigurationWidget(true); } KisPalettizeWidget::KisPalettizeWidget(QWidget* parent) : KisConfigWidget(parent) { - QGridLayout* layout = new QGridLayout(this); - layout->setColumnStretch(0, 0); - layout->setColumnStretch(1, 1); - - KisElidedLabel* paletteLabel = new KisElidedLabel(i18n("Palette"), Qt::ElideRight, this); - layout->addWidget(paletteLabel, 0, 0); - KisIconWidget* paletteIcon = new KisIconWidget(this); - paletteIcon->setFixedSize(32, 32); - - m_paletteWidget = new KisResourceItemChooser(ResourceType::Palettes, false, this); - paletteIcon->setPopupWidget(m_paletteWidget); - QObject::connect(m_paletteWidget, &KisResourceItemChooser::resourceSelected, paletteIcon, &KisIconWidget::setResource); + setupUi(this); + + paletteIconWidget->setFixedSize(32, 32); + m_paletteWidget = new KisResourceItemChooser(ResourceType::Palettes, this); + paletteIconWidget->setPopupWidget(m_paletteWidget); + QObject::connect(m_paletteWidget, &KisResourceItemChooser::resourceSelected, paletteIconWidget, &KisIconWidget::setResource); QObject::connect(m_paletteWidget, &KisResourceItemChooser::resourceSelected, this, &KisConfigWidget::sigConfigurationItemChanged); - paletteLabel->setBuddy(paletteIcon); - layout->addWidget(paletteIcon, 0, 1, Qt::AlignLeft); - - m_ditherGroupBox = new QGroupBox(i18n("Dither"), this); - m_ditherGroupBox->setCheckable(true); - QGridLayout* ditherLayout = new QGridLayout(m_ditherGroupBox); - QObject::connect(m_ditherGroupBox, &QGroupBox::toggled, this, &KisConfigWidget::sigConfigurationItemChanged); - ditherLayout->setColumnStretch(0, 0); - ditherLayout->setColumnStretch(1, 1); - layout->addWidget(m_ditherGroupBox, 1, 0, 1, 2); - - QRadioButton* ditherPatternRadio = new QRadioButton(i18n("Pattern"), this); - ditherLayout->addWidget(ditherPatternRadio, 0, 0); - KisIconWidget* ditherPatternIcon = new KisIconWidget(this); - ditherPatternIcon->setFixedSize(32, 32); - - m_ditherPatternWidget = new KisResourceItemChooser(ResourceType::Patterns, false, this); - ditherPatternIcon->setPopupWidget(m_ditherPatternWidget); - QObject::connect(m_ditherPatternWidget, &KisResourceItemChooser::resourceSelected, ditherPatternIcon, &KisIconWidget::setResource); + + QObject::connect(colorspaceComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged); + + QObject::connect(ditherGroupBox, &QGroupBox::toggled, this, &KisConfigWidget::sigConfigurationItemChanged); + + patternIconWidget->setFixedSize(32, 32); + m_ditherPatternWidget = new KisResourceItemChooser(ResourceType::Patterns, this); + patternIconWidget->setPopupWidget(m_ditherPatternWidget); + QObject::connect(m_ditherPatternWidget, &KisResourceItemChooser::resourceSelected, patternIconWidget, &KisIconWidget::setResource); QObject::connect(m_ditherPatternWidget, &KisResourceItemChooser::resourceSelected, this, &KisConfigWidget::sigConfigurationItemChanged); - ditherLayout->addWidget(ditherPatternIcon, 0, 1, Qt::AlignLeft); - m_ditherPatternUseAlphaCheckBox = new QCheckBox(i18n("Use alpha"), this); - QObject::connect(m_ditherPatternUseAlphaCheckBox, &QCheckBox::toggled, this, &KisConfigWidget::sigConfigurationItemChanged); - ditherLayout->addWidget(m_ditherPatternUseAlphaCheckBox, 0, 2, Qt::AlignLeft); - - QRadioButton* ditherNoiseRadio = new QRadioButton(i18n("Noise"), this); - ditherLayout->addWidget(ditherNoiseRadio, 1, 0); - m_ditherNoiseSeedWidget = new QLineEdit(this); - m_ditherNoiseSeedWidget->setValidator(new QIntValidator(this)); - QObject::connect(m_ditherNoiseSeedWidget, &QLineEdit::textChanged, this, &KisConfigWidget::sigConfigurationItemChanged); - ditherLayout->addWidget(m_ditherNoiseSeedWidget, 1, 1, 1, 2); - - KisElidedLabel* ditherWeightLabel = new KisElidedLabel(i18n("Weight"), Qt::ElideRight, this); - ditherLayout->addWidget(ditherWeightLabel, 2, 0); - m_ditherWeightWidget = new KisDoubleWidget(this); - m_ditherWeightWidget->setRange(0.0, 1.0); - m_ditherWeightWidget->setSingleStep(0.0625); - m_ditherWeightWidget->setPageStep(0.25); - QObject::connect(m_ditherWeightWidget, &KisDoubleWidget::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged); - ditherWeightLabel->setBuddy(m_ditherWeightWidget); - ditherLayout->addWidget(m_ditherWeightWidget, 2, 1, 1, 2); - - m_ditherModeGroup = new QButtonGroup(this); - m_ditherModeGroup->addButton(ditherPatternRadio, 0); - m_ditherModeGroup->addButton(ditherNoiseRadio, 1); - QObject::connect(m_ditherModeGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &KisConfigWidget::sigConfigurationItemChanged); - - layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Preferred, QSizePolicy::Expanding), 2, 0, 1, 2); -} -void KisPalettizeWidget::setConfiguration(const KisPropertiesConfigurationSP config) -{ - KoColorSetSP palette = KoResourceServerProvider::instance()->paletteServer()->resourceByName(config->getString("palette")); - if (palette) m_paletteWidget->setCurrentResource(palette); + QObject::connect(patternValueModeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged); - m_ditherGroupBox->setChecked(config->getBool("ditherEnabled")); + noiseSeedLineEdit->setValidator(new QIntValidator(this)); + QObject::connect(noiseSeedLineEdit, &QLineEdit::textChanged, this, &KisConfigWidget::sigConfigurationItemChanged); - QAbstractButton* ditherModeButton = m_ditherModeGroup->button(config->getInt("ditherMode")); - if (ditherModeButton) ditherModeButton->setChecked(true); + QObject::connect(noiseSeedRandomizeButton, &QToolButton::clicked, [this](){ + noiseSeedLineEdit->setText(QString::number(rand())); + }); - KoPatternSP ditherPattern = KoResourceServerProvider::instance()->patternServer()->resourceByName(config->getString("ditherPattern")); - if (ditherPattern) m_ditherPatternWidget->setCurrentResource(ditherPattern); + QObject::connect(thresholdModeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged); - m_ditherPatternUseAlphaCheckBox->setChecked(config->getBool("ditherPatternUseAlpha")); + QObject::connect(colorModeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged); - m_ditherNoiseSeedWidget->setText(QString::number(config->getInt("ditherNoiseSeed"))); + offsetScaleSpinBox->setPrefix(QString("%1 ").arg(i18n("Offset Scale:"))); + offsetScaleSpinBox->setRange(0.0, 1.0, 3); + offsetScaleSpinBox->setSingleStep(0.125); + QObject::connect(offsetScaleSpinBox, &KisDoubleSliderSpinBox::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged); - m_ditherWeightWidget->setValue(config->getDouble("ditherWeight")); + spreadSpinBox->setPrefix(QString("%1 ").arg(i18n("Spread:"))); + spreadSpinBox->setRange(0.0, 1.0, 3); + spreadSpinBox->setSingleStep(0.125); + QObject::connect(spreadSpinBox, &KisDoubleSliderSpinBox::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged); + + QObject::connect(alphaGroupBox, &QGroupBox::toggled, this, &KisConfigWidget::sigConfigurationItemChanged); + + QObject::connect(alphaModeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged); + + alphaClipSpinBox->setPrefix(QString("%1 ").arg(i18n("Clip:"))); + alphaClipSpinBox->setRange(0.0, 1.0, 3); + alphaClipSpinBox->setSingleStep(0.125); + QObject::connect(alphaClipSpinBox, &KisDoubleSliderSpinBox::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged); + + alphaIndexSpinBox->setPrefix(QString("%1 ").arg(i18n("Index:"))); + alphaIndexSpinBox->setRange(0, 255); + QObject::connect(alphaIndexSpinBox, &KisSliderSpinBox::valueChanged, this, &KisConfigWidget::sigConfigurationItemChanged); + QObject::connect(m_paletteWidget, &KisResourceItemChooser::resourceSelected, [this](){ + KoColorSetSP palette = m_paletteWidget->currentResource().staticCast(); + alphaIndexSpinBox->setMaximum(palette ? int(palette->colorCount() - 1) : 0); + alphaIndexSpinBox->setValue(std::min(alphaIndexSpinBox->value(), alphaIndexSpinBox->maximum())); + }); +} + +void KisPalettizeWidget::setConfiguration(const KisPropertiesConfigurationSP config) +{ + KoColorSetSP palette = KoResourceServerProvider::instance()->paletteServer()->resourceByName(config->getString("palette")); + if (palette) m_paletteWidget->setCurrentResource(palette); + colorspaceComboBox->setCurrentIndex(config->getInt("colorspace")); + ditherGroupBox->setChecked(config->getBool("ditherEnabled")); + thresholdModeComboBox->setCurrentIndex(config->getInt("thresholdMode")); + KoPatternSP pattern = KoResourceServerProvider::instance()->patternServer()->resourceByName(config->getString("pattern")); + if (pattern) m_ditherPatternWidget->setCurrentResource(pattern); + patternValueModeComboBox->setCurrentIndex(config->getInt("patternValueMode")); + noiseSeedLineEdit->setText(QString::number(config->getInt("noiseSeed"))); + colorModeComboBox->setCurrentIndex(config->getInt("colorMode")); + offsetScaleSpinBox->setValue(config->getDouble("offsetScale")); + spreadSpinBox->setValue(config->getDouble("spread")); + alphaGroupBox->setChecked(config->getBool("alphaEnabled")); + alphaModeComboBox->setCurrentIndex(config->getInt("alphaMode")); + alphaClipSpinBox->setValue(config->getDouble("alphaClip")); + alphaIndexSpinBox->setValue(config->getInt("alphaIndex")); } KisPropertiesConfigurationSP KisPalettizeWidget::configuration() const { KisFilterConfigurationSP config = new KisFilterConfiguration("palettize", 1); + if (m_paletteWidget->currentResource()) config->setProperty("palette", QVariant(m_paletteWidget->currentResource()->name())); - config->setProperty("ditherEnabled", m_ditherGroupBox->isChecked()); - config->setProperty("ditherMode", m_ditherModeGroup->checkedId()); - if (m_ditherPatternWidget->currentResource()) config->setProperty("ditherPattern", QVariant(m_ditherPatternWidget->currentResource()->name())); - config->setProperty("ditherPatternUseAlpha", m_ditherPatternUseAlphaCheckBox->isChecked()); - config->setProperty("ditherNoiseSeed", m_ditherNoiseSeedWidget->text().toInt()); - config->setProperty("ditherWeight", m_ditherWeightWidget->value()); + config->setProperty("colorspace", colorspaceComboBox->currentIndex()); + config->setProperty("ditherEnabled", ditherGroupBox->isChecked()); + config->setProperty("thresholdMode",thresholdModeComboBox->currentIndex()); + if (m_ditherPatternWidget->currentResource()) config->setProperty("pattern", QVariant(m_ditherPatternWidget->currentResource()->name())); + config->setProperty("patternValueMode", patternValueModeComboBox->currentIndex()); + config->setProperty("noiseSeed", noiseSeedLineEdit->text().toInt()); + config->setProperty("colorMode", colorModeComboBox->currentIndex()); + config->setProperty("offsetScale", offsetScaleSpinBox->value()); + config->setProperty("spread", spreadSpinBox->value()); + config->setProperty("alphaEnabled", alphaGroupBox->isChecked()); + config->setProperty("alphaMode", alphaModeComboBox->currentIndex()); + config->setProperty("alphaClip", alphaClipSpinBox->value()); + config->setProperty("alphaIndex", alphaIndexSpinBox->value()); return config; } KisConfigWidget* KisFilterPalettize::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev, bool useForMasks) const { Q_UNUSED(dev) Q_UNUSED(useForMasks) return new KisPalettizeWidget(parent); } KisFilterConfigurationSP KisFilterPalettize::factoryConfiguration() const { KisFilterConfigurationSP config = new KisFilterConfiguration("palettize", 1); + config->setProperty("palette", "Default"); + config->setProperty("colorspace", Colorspace::Lab); config->setProperty("ditherEnabled", false); - config->setProperty("ditherMode", DitherMode::Pattern); - config->setProperty("ditherPattern", "Grid01.pat"); - config->setProperty("ditherPatternUseAlpha", true); - config->setProperty("ditherNoiseSeed", rand()); - config->setProperty("ditherWeight", 1.0); + config->setProperty("thresholdMode", ThresholdMode::Pattern); + config->setProperty("pattern", "DITH 0202 GEN "); + config->setProperty("patternValueMode", PatternValueMode::Auto); + config->setProperty("noiseSeed", rand()); + config->setProperty("colorMode", ColorMode::PerChannelOffset); + config->setProperty("offsetScale", 0.125); + config->setProperty("spread", 1.0); + config->setProperty("alphaEnabled", true); + config->setProperty("alphaMode", AlphaMode::Clip); + config->setProperty("alphaClip", 0.5); + config->setProperty("alphaIndex", 0); return config; } void KisFilterPalettize::processImpl(KisPaintDeviceSP device, const QRect& applyRect, const KisFilterConfigurationSP config, KoUpdater* progressUpdater) const { const KoColorSetSP palette = KoResourceServerProvider::instance()->paletteServer()->resourceByName(config->getString("palette")); + const int searchColorspace = config->getInt("colorspace"); const bool ditherEnabled = config->getBool("ditherEnabled"); - const int ditherMode = config->getInt("ditherMode"); - const KoPatternSP ditherPattern = KoResourceServerProvider::instance()->patternServer()->resourceByName(config->getString("ditherPattern")); - const bool ditherPatternUseAlpha = config->getBool("ditherPatternUseAlpha"); - const quint64 ditherNoiseSeed = quint64(config->getInt("ditherNoiseSeed")); - const double ditherWeight = config->getDouble("ditherWeight"); - - const KoColorSpace* cs = device->colorSpace(); - KisRandomGenerator random(ditherNoiseSeed); - - using TreeColor = boost::geometry::model::point; - using TreeValue = std::pair>; - using Rtree = boost::geometry::index::rtree>; - Rtree m_rtree; + const int thresholdMode = config->getInt("thresholdMode"); + const KoPatternSP pattern = KoResourceServerProvider::instance()->patternServer()->resourceByName(config->getString("pattern")); + const int patternValueMode = config->getInt("patternValueMode"); + const quint64 noiseSeed = quint64(config->getInt("noiseSeed")); + const int colorMode = config->getInt("colorMode"); + const double offsetScale = config->getDouble("offsetScale"); + const double spread = config->getDouble("spread"); + const bool alphaEnabled = config->getBool("alphaEnabled"); + const int alphaMode = config->getInt("alphaMode"); + const double alphaClip = config->getDouble("alphaClip"); + const int alphaIndex = config->getInt("alphaIndex"); + + const KoColorSpace* colorspace = device->colorSpace(); + const KoColorSpace* workColorspace = (searchColorspace == Colorspace::Lab + ? KoColorSpaceRegistry::instance()->lab16() + : KoColorSpaceRegistry::instance()->rgb16("sRGB-elle-V2-srgbtrc.icc")); + + const quint8 colorCount = ditherEnabled && colorMode == ColorMode::NearestColors ? 2 : 1; + + KisRandomGenerator random(noiseSeed); + + using SearchColor = boost::geometry::model::point; + struct ColorCandidate { + KoColor color; + quint16 index; + double distance; + }; + using SearchEntry = std::pair; + boost::geometry::index::rtree> rtree; if (palette) { + // Add palette colors to search tree quint16 index = 0; for (int row = 0; row < palette->rowCount(); ++row) { for (int column = 0; column < palette->columnCount(); ++column) { KisSwatch swatch = palette->getColorGlobal(column, row); if (swatch.isValid()) { - KoColor color = swatch.color().convertedTo(cs); - TreeColor searchColor; - KoColor tempColor; - cs->toLabA16(color.data(), tempColor.data(), 1); - memcpy(reinterpret_cast(&searchColor), tempColor.data(), sizeof(TreeColor)); + KoColor color = swatch.color().convertedTo(colorspace); + KoColor workColor = swatch.color().convertedTo(workColorspace); + SearchColor searchColor; + memcpy(&searchColor, workColor.data(), sizeof(SearchColor)); // Don't add duplicates so won't dither between identical colors - std::vector result; - m_rtree.query(boost::geometry::index::contains(searchColor), std::back_inserter(result)); - if (result.empty()) m_rtree.insert(std::make_pair(searchColor, std::make_pair(color, index++))); + std::vector result; + rtree.query(boost::geometry::index::contains(searchColor), std::back_inserter(result)); + if (result.empty()) rtree.insert(SearchEntry(searchColor, {color, index, 0.0})); } + ++index; } } - } - KisSequentialIteratorProgress it(device, applyRect, progressUpdater); - while (it.nextPixel()) { - // Find 2 nearest palette colors to pixel color - TreeColor imageColor; - KoColor tempColor; - cs->toLabA16(it.oldRawData(), tempColor.data(), 1); - memcpy(reinterpret_cast(&imageColor), tempColor.data(), sizeof(TreeColor)); - std::vector nearestColors; - nearestColors.reserve(2); - for (Rtree::const_query_iterator it = m_rtree.qbegin(boost::geometry::index::nearest(imageColor, 2)); it != m_rtree.qend(); ++it) { - nearestColors.push_back(*it); + // Automatically pick between lightness-based and alpha-based patterns by whichever has maximum range + bool patternUseAlpha; + if (pattern && ditherEnabled && thresholdMode == ThresholdMode::Pattern && patternValueMode == PatternValueMode::Auto) { + qreal lightnessMin = 1.0, lightnessMax = 0.0; + qreal alphaMin = 1.0, alphaMax = 0.0; + const QImage &image = pattern->pattern(); + for (int y = 0; y < image.height(); ++y) { + for (int x = 0; x < image.width(); ++x) { + const QColor pixel = image.pixelColor(x, y); + lightnessMin = std::min(lightnessMin, pixel.lightnessF()); + lightnessMax = std::max(lightnessMax, pixel.lightnessF()); + alphaMin = std::min(alphaMin, pixel.alphaF()); + alphaMax = std::max(alphaMax, pixel.alphaF()); + } + } + patternUseAlpha = (alphaMax - alphaMin > lightnessMax - lightnessMin); } + else { + patternUseAlpha = (patternValueMode == PatternValueMode::Alpha); + } + + KisSequentialIteratorProgress pixel(device, applyRect, progressUpdater); + while (pixel.nextPixel()) { + KoColor workColor(pixel.oldRawData(), colorspace); + workColor.convertTo(workColorspace); + + // Find dither threshold + double ditherPos = 0.5; + if (pattern && ditherEnabled) { + if (thresholdMode == ThresholdMode::Pattern) { + const QImage &image = pattern->pattern(); + const QColor ditherPixel = image.pixelColor(pixel.x() % image.width(), pixel.y() % image.height()); + ditherPos = (patternUseAlpha ? ditherPixel.alphaF() : ditherPixel.lightnessF()); + } + else if (thresholdMode == ThresholdMode::Noise) { + ditherPos = random.doubleRandomAt(pixel.x(), pixel.y()); + } - if (nearestColors.size() > 0) { - size_t nearestIndex; - // Dither not enabled or only one color found so don't dither - if (!ditherEnabled || nearestColors.size() == 1) nearestIndex = 0; - // Otherwise threshold between colors based on relative distance + // Traditional per-channel ordered dithering + if (colorMode == ColorMode::PerChannelOffset) { + QVector normalized(int(workColorspace->channelCount())); + workColorspace->normalisedChannelsValue(workColor.data(), normalized); + for (int channel = 0; channel < int(workColorspace->channelCount()); ++channel) { + normalized[channel] += (ditherPos - 0.5) * offsetScale; + } + workColorspace->fromNormalisedChannelsValue(workColor.data(), normalized); + } + } + const double ditherThreshold = (0.5 - (spread / 2.0) + ditherPos * spread); + + // Get candidate colors and their distances + SearchColor searchColor; + memcpy(reinterpret_cast(&searchColor), workColor.data(), sizeof(SearchColor)); + std::vector candidateColors; + candidateColors.reserve(size_t(colorCount)); + double distanceSum = 0.0; + for (auto it = rtree.qbegin(boost::geometry::index::nearest(searchColor, colorCount)); it != rtree.qend() && candidateColors.size() < colorCount; ++it) { + ColorCandidate candidate = it->second; + candidate.distance = boost::geometry::distance(searchColor, it->first); + candidateColors.push_back(candidate); + distanceSum += candidate.distance; + } + + // Select color candidate + quint16 selected; + if (ditherEnabled && colorMode == ColorMode::NearestColors) { + // Sort candidates by palette order for stable dither color ordering + const bool swap = candidateColors[0].index > candidateColors[1].index; + selected = swap ^ (candidateColors[swap].distance / distanceSum > ditherThreshold); + } else { - std::vector distances(nearestColors.size()); - double distanceSum = 0.0; - for (size_t i = 0; i < nearestColors.size(); ++i) { - distances[i] = boost::geometry::distance(imageColor, nearestColors[i].first); - distanceSum += distances[i]; + selected = 0; + } + ColorCandidate &candidate = candidateColors[selected]; + + // Set alpha + const double oldAlpha = colorspace->opacityF(pixel.oldRawData()); + double newAlpha = oldAlpha; + if (alphaEnabled && !(!ditherEnabled && alphaMode == AlphaMode::UseDither)) { + if (alphaMode == AlphaMode::Clip) { + newAlpha = oldAlpha < alphaClip? 0.0 : 1.0; } - // Use palette ordering for stable dither color threshold ordering - size_t ditherIndices[2] = {0, 1}; - if (nearestColors[ditherIndices[0]].second.second > nearestColors[ditherIndices[1]].second.second) std::swap(ditherIndices[0], ditherIndices[1]); - const double pos = distances[ditherIndices[0]] / distanceSum; - double threshold = 0.5; - if (ditherMode == DitherMode::Pattern) { - const QImage &image = ditherPattern->pattern(); - const QColor pixel = image.pixelColor(it.x() % image.width(), it.y() % image.height()); - threshold = ditherPatternUseAlpha ? pixel.alphaF() : pixel.lightnessF(); + else if (alphaMode == AlphaMode::Index) { + newAlpha = (candidate.index == alphaIndex ? 0.0 : 1.0); } - else if (ditherMode == DitherMode::Noise) { - threshold = random.doubleRandomAt(it.x(), it.y()); + else if (alphaMode == AlphaMode::UseDither) { + if (colorMode == ColorMode::PerChannelOffset) { + newAlpha = colorspace->opacityF(workColor.convertedTo(colorspace).data()) < 0.5 ? 0.0 : 1.0; + } + else if (colorMode == ColorMode::NearestColors) { + newAlpha = oldAlpha < ditherThreshold ? 0.0 : 1.0; + } } - nearestIndex = pos < (0.5 - (ditherWeight / 2.0) + threshold * ditherWeight) ? ditherIndices[0] : ditherIndices[1]; } - memcpy(it.rawData(), nearestColors[nearestIndex].second.first.data(), cs->pixelSize()); + colorspace->setOpacity(candidate.color.data(), newAlpha, 1); + + // Copy color to pixel + memcpy(pixel.rawData(), candidate.color.data(), colorspace->pixelSize()); } } } - -#include "palettize.moc" diff --git a/plugins/filters/palettize/palettize.h b/plugins/filters/palettize/palettize.h index 0669f0003a..d01447835a 100644 --- a/plugins/filters/palettize/palettize.h +++ b/plugins/filters/palettize/palettize.h @@ -1,77 +1,85 @@ /* * This file is part of the KDE project * * Copyright (c) 2019 Carl Olsson * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PALETTIZE_H #define PALETTIZE_H +#include "ui_palettize.h" + #include #include #include #include #include #include #include -class KoColorSet; class KisResourceItemChooser; -class QGroupBox; -class KoPattern; -class QCheckBox; -class QLineEdit; -class QButtonGroup; -class KisDoubleWidget; class Palettize : public QObject { public: Palettize(QObject *parent, const QVariantList &); }; -class KisPalettizeWidget : public KisConfigWidget +class KisPalettizeWidget : public KisConfigWidget, public Ui::Palettize { public: KisPalettizeWidget(QWidget* parent = 0); void setConfiguration(const KisPropertiesConfigurationSP) override; KisPropertiesConfigurationSP configuration() const override; private: KisResourceItemChooser* m_paletteWidget; - QGroupBox* m_ditherGroupBox; - QButtonGroup* m_ditherModeGroup; KisResourceItemChooser* m_ditherPatternWidget; - QCheckBox* m_ditherPatternUseAlphaCheckBox; - QLineEdit* m_ditherNoiseSeedWidget; - KisDoubleWidget* m_ditherWeightWidget; }; class KisFilterPalettize : public KisFilter { public: - enum DitherMode { + enum Colorspace { + Lab, + RGB + }; + enum AlphaMode { + Clip, + Index, + UseDither + }; + enum ThresholdMode { Pattern, Noise }; + enum PatternValueMode { + Auto, + Lightness, + Alpha + }; + enum ColorMode { + PerChannelOffset, + NearestColors + }; KisFilterPalettize(); static inline KoID id() { return KoID("palettize", i18n("Palettize")); } KisConfigWidget* createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool useForMasks) const override; KisFilterConfigurationSP factoryConfiguration() const override; void processImpl(KisPaintDeviceSP device, const QRect &applyRect, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const override; }; #endif diff --git a/plugins/filters/palettize/palettize.ui b/plugins/filters/palettize/palettize.ui new file mode 100644 index 0000000000..ffdc646182 --- /dev/null +++ b/plugins/filters/palettize/palettize.ui @@ -0,0 +1,467 @@ + + + Palettize + + + + 0 + 0 + 286 + 468 + + + + Palettize + + + + + + + 0 + 0 + + + + Palette + + + paletteIconWidget + + + + + + + ... + + + + + + + Colorspace + + + colorspaceComboBox + + + + + + + + Lab + + + + + RGB + + + + + + + + Dither + + + true + + + + + + + 0 + 0 + + + + Threshold Mode + + + thresholdModeComboBox + + + + + + + + Pattern + + + + + Noise + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + 1 + + + Amount: + + + + + + + + 0 + 0 + + + + Pattern + + + patternIconWidget + + + + + + + ... + + + + + + + Value Mode + + + patternValueModeComboBox + + + + + + + + Auto + + + + + Lightness + + + + + Alpha + + + + + + + + + + + + Seed + + + noiseSeedLineEdit + + + + + + + + + + Randomize + + + Qt::ToolButtonTextOnly + + + + + + + + + + + + 0 + 0 + + + + Color Mode + + + colorModeComboBox + + + + + + + + Per Channel Offset + + + + + Nearest Colors + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + Alpha + + + true + + + + + + + 0 + 0 + + + + Alpha Mode + + + alphaModeComboBox + + + + + + + + Clip + + + + + Index + + + + + Use Dither + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + 0 + + + Amount: + + + + + + + + + + + + + + + + + + + + + + + + + + KisIconWidget + QToolButton +
kis_iconwidget.h
+
+ + KisDoubleSliderSpinBox + QDoubleSpinBox +
kis_slider_spin_box.h
+
+ + KisSliderSpinBox + QSpinBox +
kis_slider_spin_box.h
+
+
+ + + + thresholdModeComboBox + currentIndexChanged(int) + thresholdModeStackedWidget + setCurrentIndex(int) + + + 264 + 163 + + + 229 + 173 + + + + + thresholdModeStackedWidget + currentChanged(int) + thresholdModeComboBox + setCurrentIndex(int) + + + 264 + 246 + + + 264 + 163 + + + + + colorModeComboBox + currentIndexChanged(int) + colorModeStackedWidget + setCurrentIndex(int) + + + 264 + 278 + + + 264 + 331 + + + + + colorModeStackedWidget + currentChanged(int) + colorModeComboBox + setCurrentIndex(int) + + + 264 + 331 + + + 264 + 278 + + + + + alphaModeComboBox + currentIndexChanged(int) + alphaModeStackedWidget + setCurrentIndex(int) + + + 168 + 392 + + + 162 + 422 + + + + + alphaModeStackedWidget + currentChanged(int) + alphaModeComboBox + setCurrentIndex(int) + + + 86 + 422 + + + 127 + 398 + + + + +
diff --git a/plugins/impex/heightmap/kis_heightmap_export.cpp b/plugins/impex/heightmap/kis_heightmap_export.cpp index bf4554c9a8..cea427fac5 100644 --- a/plugins/impex/heightmap/kis_heightmap_export.cpp +++ b/plugins/impex/heightmap/kis_heightmap_export.cpp @@ -1,149 +1,150 @@ /* * Copyright (c) 2014 Boudewijn Rempt * Copyright (c) 2017 Victor Wåhlström * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_heightmap_export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_wdg_options_heightmap.h" #include "kis_heightmap_utils.h" K_PLUGIN_FACTORY_WITH_JSON(KisHeightMapExportFactory, "krita_heightmap_export.json", registerPlugin();) template static void writeData(KisPaintDeviceSP pd, const QRect &bounds, QDataStream &out_stream) { KIS_ASSERT_RECOVER_RETURN(pd); KisSequentialConstIterator it(pd, bounds); while (it.nextPixel()) { out_stream << KoGrayTraits::gray(const_cast(it.rawDataConst())); } } KisHeightMapExport::KisHeightMapExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisHeightMapExport::~KisHeightMapExport() { } KisPropertiesConfigurationSP KisHeightMapExport::defaultConfiguration(const QByteArray &from, const QByteArray &to) const { Q_UNUSED(from); Q_UNUSED(to); KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("endianness", 0); return cfg; } KisConfigWidget *KisHeightMapExport::createConfigurationWidget(QWidget *parent, const QByteArray &from, const QByteArray &to) const { Q_UNUSED(from); Q_UNUSED(to); bool export_mode = true; KisWdgOptionsHeightmap* wdg = new KisWdgOptionsHeightmap(parent, export_mode); return wdg; } void KisHeightMapExport::initializeCapabilities() { if (mimeType() == "image/x-r8") { QList > supportedColorModels; supportedColorModels << QPair() << QPair(GrayAColorModelID, Integer8BitsColorDepthID); addSupportedColorModels(supportedColorModels, "R8 Heightmap"); } else if (mimeType() == "image/x-r16") { QList > supportedColorModels; supportedColorModels << QPair() << QPair(GrayAColorModelID, Integer16BitsColorDepthID); addSupportedColorModels(supportedColorModels, "R16 Heightmap"); } else if (mimeType() == "image/x-r32") { QList > supportedColorModels; supportedColorModels << QPair() << QPair(GrayAColorModelID, Float32BitsColorDepthID); addSupportedColorModels(supportedColorModels, "R32 Heightmap"); } } KisImportExportErrorCode KisHeightMapExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) { KIS_ASSERT_RECOVER_RETURN_VALUE(mimeType() == "image/x-r16" || mimeType() == "image/x-r8" || mimeType() == "image/x-r32", ImportExportCodes::FileFormatIncorrect); KisImageSP image = document->savingImage(); QDataStream::ByteOrder bo = configuration->getInt("endianness", 1) == 0 ? QDataStream::BigEndian : QDataStream::LittleEndian; KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); QDataStream s(io); s.setByteOrder(bo); // needed for 32bit float data s.setFloatingPointPrecision(QDataStream::SinglePrecision); KoID target_co_model = GrayAColorModelID; KoID target_co_depth = KisHeightmapUtils::mimeTypeToKoID(mimeType()); KIS_ASSERT(!target_co_depth.id().isNull()); if (pd->colorSpace()->colorModelId() != target_co_model || pd->colorSpace()->colorDepthId() != target_co_depth) { pd = new KisPaintDevice(*pd.data()); pd->convertTo(KoColorSpaceRegistry::instance()->colorSpace(target_co_model.id(), target_co_depth.id())); } if (target_co_depth == Float32BitsColorDepthID) { writeData(pd, image->bounds(), s); } else if (target_co_depth == Integer16BitsColorDepthID) { writeData(pd, image->bounds(), s); } else if (target_co_depth == Integer8BitsColorDepthID) { writeData(pd, image->bounds(), s); } else { KIS_ASSERT_RECOVER_RETURN_VALUE(true, ImportExportCodes::InternalError); return ImportExportCodes::InternalError; } return ImportExportCodes::OK; } #include "kis_heightmap_export.moc" diff --git a/plugins/impex/heightmap/kis_heightmap_export.h b/plugins/impex/heightmap/kis_heightmap_export.h index cf966192c9..0ff29cb890 100644 --- a/plugins/impex/heightmap/kis_heightmap_export.h +++ b/plugins/impex/heightmap/kis_heightmap_export.h @@ -1,41 +1,42 @@ /* * Copyright (c) 2014 Boudewijn Rempt * Copyright (c) 2017 Victor Wåhlström * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KIS_HeightMap_EXPORT_H_ #define _KIS_HeightMap_EXPORT_H_ #include #include #include class KisHeightMapExport : public KisImportExportFilter { Q_OBJECT public: KisHeightMapExport(QObject *parent, const QVariantList &); ~KisHeightMapExport() override; KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const override; KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const override; void initializeCapabilities() override; KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override; }; #endif diff --git a/plugins/impex/ora/kis_open_raster_load_context.cpp b/plugins/impex/ora/kis_open_raster_load_context.cpp index 7a073b3426..4b06ac9d48 100644 --- a/plugins/impex/ora/kis_open_raster_load_context.cpp +++ b/plugins/impex/ora/kis_open_raster_load_context.cpp @@ -1,62 +1,63 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_open_raster_load_context.h" #include #include #include #include #include #include "kis_png_converter.h" KisOpenRasterLoadContext::KisOpenRasterLoadContext(KoStore* _store) : m_store(_store) { } KisImageSP KisOpenRasterLoadContext::loadDeviceData(const QString & filename) { if (m_store->open(filename)) { KoStoreDevice io(m_store); if (!io.open(QIODevice::ReadOnly)) { dbgFile << "Could not open for reading:" << filename; return 0; } KisPNGConverter pngConv(0); pngConv.buildImage(&io); io.close(); m_store->close(); return pngConv.image(); } return 0; } QDomDocument KisOpenRasterLoadContext::loadStack() { m_store->open("stack.xml"); KoStoreDevice io(m_store); QDomDocument doc; doc.setContent(&io, false); io.close(); m_store->close(); return doc; } diff --git a/plugins/impex/ora/kis_open_raster_save_context.cpp b/plugins/impex/ora/kis_open_raster_save_context.cpp index 142a19607b..e9ff568a0d 100644 --- a/plugins/impex/ora/kis_open_raster_save_context.cpp +++ b/plugins/impex/ora/kis_open_raster_save_context.cpp @@ -1,60 +1,61 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_open_raster_save_context.h" #include #include #include #include #include #include #include #include #include #include "kis_png_converter.h" KisOpenRasterSaveContext::KisOpenRasterSaveContext(KoStore* store) : m_id(0) , m_store(store) { } QString KisOpenRasterSaveContext::saveDeviceData(KisPaintDeviceSP dev, KisMetaData::Store* metaData, const QRect &imageRect, const qreal xRes, const qreal yRes) { QString filename = QString("data/layer%1.png").arg(m_id++); if (KisPNGConverter::saveDeviceToStore(filename, imageRect, xRes, yRes, dev, m_store, metaData)) { return filename; } return ""; } void KisOpenRasterSaveContext::saveStack(const QDomDocument& doc) { if (m_store->open("stack.xml")) { KoStoreDevice io(m_store); io.write(doc.toByteArray()); io.close(); m_store->close(); } else { dbgFile << "Opening of the stack.xml file failed :"; } } diff --git a/plugins/impex/ora/ora_converter.cpp b/plugins/impex/ora/ora_converter.cpp index 1df32a0c15..49667f9058 100644 --- a/plugins/impex/ora/ora_converter.cpp +++ b/plugins/impex/ora/ora_converter.cpp @@ -1,117 +1,118 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ora_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_png_converter.h" #include "kis_open_raster_load_context.h" #include "kis_open_raster_save_context.h" OraConverter::OraConverter(KisDocument *doc) : m_doc(doc) , m_stop(false) { } OraConverter::~OraConverter() { } KisImportExportErrorCode OraConverter::buildImage(QIODevice *io) { KoStore* store = KoStore::createStore(io, KoStore::Read, "image/openraster", KoStore::Zip); if (!store) { delete store; return ImportExportCodes::Failure; } KisOpenRasterLoadContext olc(store); KisOpenRasterStackLoadVisitor orslv(m_doc->createUndoStore(), &olc); orslv.loadImage(); m_image = orslv.image(); m_activeNodes = orslv.activeNodes(); delete store; return ImportExportCodes::OK; } KisImageSP OraConverter::image() { return m_image; } vKisNodeSP OraConverter::activeNodes() { return m_activeNodes; } KisImportExportErrorCode OraConverter::buildFile(QIODevice *io, KisImageSP image, vKisNodeSP activeNodes) { // Open file for writing KoStore* store = KoStore::createStore(io, KoStore::Write, "image/openraster", KoStore::Zip); if (!store) { delete store; return ImportExportCodes::Failure; } KisOpenRasterSaveContext osc(store); KisOpenRasterStackSaveVisitor orssv(&osc, activeNodes); image->rootLayer()->accept(orssv); if (store->open("Thumbnails/thumbnail.png")) { QSize previewSize = image->bounds().size(); previewSize.scale(QSize(256,256), Qt::KeepAspectRatio); QImage preview = image->convertToQImage(previewSize, 0); KoStoreDevice io(store); if (io.open(QIODevice::WriteOnly)) { preview.save(&io, "PNG"); } io.close(); store->close(); } KisPaintDeviceSP dev = image->projection(); KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store); delete store; return ImportExportCodes::OK; } void OraConverter::cancel() { m_stop = true; } diff --git a/plugins/impex/ora/ora_converter.h b/plugins/impex/ora/ora_converter.h index 5e1c2f0c27..b215e786e0 100644 --- a/plugins/impex/ora/ora_converter.h +++ b/plugins/impex/ora/ora_converter.h @@ -1,53 +1,54 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ORA_CONVERTER_H_ #define _ORA_CONVERTER_H_ #include #include #include "kis_png_converter.h" #include "kis_types.h" class KisDocument; class OraConverter : public QObject { Q_OBJECT public: OraConverter(KisDocument *doc); ~OraConverter() override; public: KisImportExportErrorCode buildImage(QIODevice *io); KisImportExportErrorCode buildFile(QIODevice *io, KisImageSP image, vKisNodeSP activeNodes); /** * Retrieve the constructed image */ KisImageSP image(); vKisNodeSP activeNodes(); public Q_SLOTS: virtual void cancel(); private: KisImageSP m_image; KisDocument *m_doc; vKisNodeSP m_activeNodes; bool m_stop; }; #endif diff --git a/plugins/impex/ora/ora_export.cc b/plugins/impex/ora/ora_export.cc index e352f63005..8d2d28f714 100644 --- a/plugins/impex/ora/ora_export.cc +++ b/plugins/impex/ora/ora_export.cc @@ -1,117 +1,118 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ora_export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ora_converter.h" class KisExternalLayer; K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_ora_export.json", registerPlugin();) OraExport::OraExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } OraExport::~OraExport() { } bool hasShapeLayerChild(KisNodeSP node) { if (!node) return false; Q_FOREACH (KisNodeSP child, node->childNodes(QStringList(), KoProperties())) { if (child->inherits("KisShapeLayer") || child->inherits("KisGeneratorLayer") || child->inherits("KisCloneLayer")) { return true; } else { if (hasShapeLayerChild(child)) { return true; } } } return false; } KisImportExportErrorCode OraExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/) { KisImageSP image = document->savingImage(); Q_CHECK_PTR(image); OraConverter oraConverter(document); KisImportExportErrorCode res = oraConverter.buildFile(io, image, {document->preActivatedNode()}); return res; } void OraExport::initializeCapabilities() { addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisGroupLayer")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("NodeTypeCheck/KisAdjustmentLayer")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("ColorModelHomogenousCheck")->create(KisExportCheckBase::SUPPORTED)); QList > supportedColorModels; supportedColorModels << QPair() << QPair(RGBAColorModelID, Integer8BitsColorDepthID) << QPair(RGBAColorModelID, Integer16BitsColorDepthID) << QPair(GrayAColorModelID, Integer8BitsColorDepthID) << QPair(GrayAColorModelID, Integer16BitsColorDepthID); addSupportedColorModels(supportedColorModels, "OpenRaster"); } QString OraExport::verify(const QString &fileName) const { QString error = KisImportExportFilter::verify(fileName); if (error.isEmpty()) { return KisImportExportFilter::verifyZiPBasedFiles(fileName, QStringList() << "mimetype" << "stack.xml" << "mergedimage.png"); } return error; } #include diff --git a/plugins/impex/ora/ora_export.h b/plugins/impex/ora/ora_export.h index a9e1a657d3..896d8fbf1a 100644 --- a/plugins/impex/ora/ora_export.h +++ b/plugins/impex/ora/ora_export.h @@ -1,37 +1,38 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _ORA_EXPORT_H_ #define _ORA_EXPORT_H_ #include #include class OraExport : public KisImportExportFilter { Q_OBJECT public: OraExport(QObject *parent, const QVariantList &); ~OraExport() override; public: KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override; void initializeCapabilities() override; QString verify(const QString &fileName) const override; }; #endif diff --git a/plugins/impex/ora/ora_import.cc b/plugins/impex/ora/ora_import.cc index 2d49dee39c..d558a1d3de 100644 --- a/plugins/impex/ora/ora_import.cc +++ b/plugins/impex/ora/ora_import.cc @@ -1,52 +1,53 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ora_import.h" #include #include #include #include #include "ora_converter.h" K_PLUGIN_FACTORY_WITH_JSON(ImportFactory, "krita_ora_import.json", registerPlugin();) OraImport::OraImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } OraImport::~OraImport() { } KisImportExportErrorCode OraImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/) { OraConverter oraConverter(document); KisImportExportErrorCode result = oraConverter.buildImage(io); if (result.isOk()) { document->setCurrentImage(oraConverter.image()); if (oraConverter.activeNodes().size() > 0) { document->setPreActivatedNode(oraConverter.activeNodes()[0]); } } return result; } #include diff --git a/plugins/impex/ora/ora_import.h b/plugins/impex/ora/ora_import.h index 7e8b434752..56f26ab538 100644 --- a/plugins/impex/ora/ora_import.h +++ b/plugins/impex/ora/ora_import.h @@ -1,35 +1,36 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ORA_IMPORT_H_ #define ORA_IMPORT_H_ #include #include class OraImport : public KisImportExportFilter { Q_OBJECT public: OraImport(QObject *parent, const QVariantList &); ~OraImport() override; public: KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override; }; #endif diff --git a/plugins/impex/ppm/kis_ppm_export.cpp b/plugins/impex/ppm/kis_ppm_export.cpp index 9dcb24478a..e76bccd566 100644 --- a/plugins/impex/ppm/kis_ppm_export.cpp +++ b/plugins/impex/ppm/kis_ppm_export.cpp @@ -1,316 +1,317 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_ppm_export.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include K_PLUGIN_FACTORY_WITH_JSON(KisPPMExportFactory, "krita_ppm_export.json", registerPlugin();) KisPPMExport::KisPPMExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisPPMExport::~KisPPMExport() { } class KisPPMFlow { public: KisPPMFlow() { } virtual ~KisPPMFlow() { } virtual void writeBool(quint8 v) = 0; virtual void writeBool(quint16 v) = 0; virtual void writeNumber(quint8 v) = 0; virtual void writeNumber(quint16 v) = 0; virtual void flush() = 0; private: }; class KisPPMAsciiFlow : public KisPPMFlow { public: KisPPMAsciiFlow(QIODevice* device) : m_device(device) { } ~KisPPMAsciiFlow() override { } void writeBool(quint8 v) override { if (v > 127) { m_device->write("1 "); } else { m_device->write("0 "); } } void writeBool(quint16 v) override { writeBool(quint8(v >> 8)); } void writeNumber(quint8 v) override { m_device->write(QByteArray::number(v)); m_device->write(" "); } void writeNumber(quint16 v) override { m_device->write(QByteArray::number(v)); m_device->write(" "); } void flush() override { } private: QIODevice* m_device; }; class KisPPMBinaryFlow : public KisPPMFlow { public: KisPPMBinaryFlow(QIODevice* device) : m_device(device), m_pos(0), m_current(0) { } ~KisPPMBinaryFlow() override { } void writeBool(quint8 v) override { m_current = m_current << 1; m_current |= (v > 127); ++m_pos; if (m_pos >= 8) { m_current = 0; m_pos = 0; flush(); } } void writeBool(quint16 v) override { writeBool(quint8(v >> 8)); } void writeNumber(quint8 v) override { m_device->write((char*)&v, 1); } void writeNumber(quint16 v) override { quint16 vo = qToBigEndian(v); m_device->write((char*)&vo, 2); } void flush() override { m_device->write((char*)&m_current, 1); } private: QIODevice* m_device; int m_pos; quint8 m_current; }; KisImportExportErrorCode KisPPMExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) { bool rgb = (mimeType() == "image/x-portable-pixmap"); bool binary = (configuration->getInt("type") == 0); bool bitmap = (mimeType() == "image/x-portable-bitmap"); KisImageSP image = document->savingImage(); KisPaintDeviceSP pd = new KisPaintDevice(*image->projection()); // Test color space if (((rgb && (pd->colorSpace()->id() != "RGBA" && pd->colorSpace()->id() != "RGBA16")) || (!rgb && (pd->colorSpace()->id() != "GRAYA" && pd->colorSpace()->id() != "GRAYA16" && pd->colorSpace()->id() != "GRAYAU16")))) { if (rgb) { pd->convertTo(KoColorSpaceRegistry::instance()->rgb8(0), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } else { pd->convertTo(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } } bool is16bit = pd->colorSpace()->id() == "RGBA16" || pd->colorSpace()->id() == "GRAYAU16"; // Write the magic QString toWrite = ""; int written = 0; if (rgb) { if (binary) { toWrite = "P6"; } else { toWrite = "P3"; } } else if (bitmap) { if (binary) { toWrite = "P4"; } else { toWrite = "P1"; } } else { if (binary) { toWrite = "P5"; } else { toWrite = "P2"; } } written = io->write(toWrite.toUtf8()); if (written != toWrite.toUtf8().length()) { return ImportExportCodes::ErrorWhileWriting; } written = io->write("\n"); // Write the header QByteArray width = QByteArray::number(image->width()); QByteArray height = QByteArray::number(image->height()); written += io->write(width); written += io->write(" "); written += io->write(height); if (written != QString(" ").length() + QString("\n").length() + width.length() + height.length()) { return ImportExportCodes::ErrorWhileWriting; } if (!bitmap) { if (is16bit) { toWrite = " 65535\n"; } else { toWrite = " 255\n"; } } else { toWrite = "\n"; } written = io->write(toWrite.toUtf8()); if (written != toWrite.toUtf8().length()) { return ImportExportCodes::ErrorWhileWriting; } // Write the data KisPPMFlow* flow = 0; if (binary) flow = new KisPPMBinaryFlow(io); else flow = new KisPPMAsciiFlow(io); for (int y = 0; y < image->height(); ++y) { KisHLineIteratorSP it = pd->createHLineIteratorNG(0, y, image->width()); if (is16bit) { if (rgb) { do { flow->writeNumber(KoBgrU16Traits::red(it->rawData())); flow->writeNumber(KoBgrU16Traits::green(it->rawData())); flow->writeNumber(KoBgrU16Traits::blue(it->rawData())); } while (it->nextPixel()); } else if (bitmap) { do { flow->writeBool(*reinterpret_cast(it->rawData())); } while (it->nextPixel()); } else { do { flow->writeNumber(*reinterpret_cast(it->rawData())); } while (it->nextPixel()); } } else { if (rgb) { do { flow->writeNumber(KoBgrTraits::red(it->rawData())); flow->writeNumber(KoBgrTraits::green(it->rawData())); flow->writeNumber(KoBgrTraits::blue(it->rawData())); } while (it->nextPixel()); } else if (bitmap) { do { flow->writeBool(*reinterpret_cast(it->rawData())); } while (it->nextPixel()); } else { do { flow->writeNumber(*reinterpret_cast(it->rawData())); } while (it->nextPixel()); } } } if (bitmap) { flow->flush(); } delete flow; return ImportExportCodes::OK; } KisPropertiesConfigurationSP KisPPMExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("type", 0); return cfg; } KisConfigWidget *KisPPMExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const { return new KisWdgOptionsPPM(parent); } void KisWdgOptionsPPM::setConfiguration(const KisPropertiesConfigurationSP cfg) { cmbType->setCurrentIndex(cfg->getInt("type", 0)); } KisPropertiesConfigurationSP KisWdgOptionsPPM::configuration() const { KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration(); cfg->setProperty("type", cmbType->currentIndex()); return cfg; } void KisPPMExport::initializeCapabilities() { addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + RGBAColorModelID.id() + "/" + Integer8BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + RGBAColorModelID.id() + "/" + Integer16BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + GrayAColorModelID.id() + "/" + Integer8BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED)); addCapability(KisExportCheckRegistry::instance()->get("ColorModelCheck/" + GrayAColorModelID.id() + "/" + Integer16BitsColorDepthID.id())->create(KisExportCheckBase::SUPPORTED)); QList > supportedColorModels; supportedColorModels << QPair() << QPair(RGBAColorModelID, Integer8BitsColorDepthID) << QPair(RGBAColorModelID, Integer16BitsColorDepthID) << QPair(GrayAColorModelID, Integer8BitsColorDepthID) << QPair(GrayAColorModelID, Integer16BitsColorDepthID); addSupportedColorModels(supportedColorModels, "PPM"); } #include "kis_ppm_export.moc" diff --git a/plugins/impex/ppm/kis_ppm_export.h b/plugins/impex/ppm/kis_ppm_export.h index 53f15c5e70..070db83b7a 100644 --- a/plugins/impex/ppm/kis_ppm_export.h +++ b/plugins/impex/ppm/kis_ppm_export.h @@ -1,58 +1,59 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KIS_PPM_EXPORT_H_ #define _KIS_PPM_EXPORT_H_ #include #include #include #include "ui_kis_wdg_options_ppm.h" class KisWdgOptionsPPM : public KisConfigWidget, public Ui::WdgOptionsPPM { Q_OBJECT public: KisWdgOptionsPPM(QWidget *parent) : KisConfigWidget(parent) { setupUi(this); } void setConfiguration(const KisPropertiesConfigurationSP cfg) override; KisPropertiesConfigurationSP configuration() const override; }; class KisPPMExport : public KisImportExportFilter { Q_OBJECT public: KisPPMExport(QObject *parent, const QVariantList &); ~KisPPMExport() override; public: KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override; KisPropertiesConfigurationSP defaultConfiguration(const QByteArray& from = "", const QByteArray& to = "") const override; KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray& from = "", const QByteArray& to = "") const override; void initializeCapabilities() override; }; #endif diff --git a/plugins/impex/qml/qml_converter.cc b/plugins/impex/qml/qml_converter.cc index 4d98234f0a..48b9440982 100644 --- a/plugins/impex/qml/qml_converter.cc +++ b/plugins/impex/qml/qml_converter.cc @@ -1,96 +1,97 @@ /* * Copyright (c) 2013 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qml_converter.h" #include #include #include #include #define SPACE " " QMLConverter::QMLConverter() { } QMLConverter::~QMLConverter() { } KisImportExportErrorCode QMLConverter::buildFile(const QString &filename, const QString &realFilename, QIODevice *io, KisImageSP image) { QTextStream out(io); out.setCodec("UTF-8"); out << "import QtQuick 1.1" << "\n\n"; out << "Rectangle {\n"; writeInt(out, 1, "width", image->width()); writeInt(out, 1, "height", image->height()); out << "\n"; QFileInfo info(filename); QFileInfo infoRealFile(realFilename); KisNodeSP node = image->rootLayer()->firstChild(); QString imageDir = infoRealFile.baseName() + "_images"; QString imagePath = infoRealFile.absolutePath() + '/' + imageDir; if (node) { QDir dir; bool success = dir.mkpath(imagePath); if (!success) { return ImportExportCodes::CannotCreateFile; } } dbgFile << "Saving images to " << imagePath; while(node) { KisPaintDeviceSP projection = node->projection(); QRect rect = projection->exactBounds(); QImage qmlImage = projection->convertToQImage(0, rect.x(), rect.y(), rect.width(), rect.height()); QString name = node->name().replace(' ', '_').toLower(); QString fileName = name + ".png"; qmlImage.save(imagePath +'/'+ fileName); out << SPACE << "Image {\n"; writeString(out, 2, "id", name); writeInt(out, 2, "x", rect.x()); writeInt(out, 2, "y", rect.y()); writeInt(out, 2, "width", rect.width()); writeInt(out, 2, "height", rect.height()); writeString(out, 2, "source", "\"" + imageDir + '/' + fileName + "\"" ); writeString(out, 2, "opacity", QString().setNum(node->opacity()/255.0)); out << SPACE << "}\n"; node = node->nextSibling(); } out << "}\n"; return ImportExportCodes::OK; } void QMLConverter::writeString(QTextStream& out, int spacing, const QString& setting, const QString& value) { for (int space = 0; space < spacing; space++) { out << SPACE; } out << setting << ": " << value << "\n"; } void QMLConverter::writeInt(QTextStream& out, int spacing, const QString& setting, int value) { writeString(out, spacing, setting, QString::number(value)); } diff --git a/plugins/impex/qml/qml_converter.h b/plugins/impex/qml/qml_converter.h index ab03696947..f0b1ccb122 100644 --- a/plugins/impex/qml/qml_converter.h +++ b/plugins/impex/qml/qml_converter.h @@ -1,44 +1,45 @@ /* * Copyright (c) 2013 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _QML_CONVERTER_H_ #define _QML_CONVERTER_H_ #include #include #include #include "kis_types.h" #include class QMLConverter : public QObject { Q_OBJECT public: QMLConverter(); ~QMLConverter() override; public: KisImportExportErrorCode buildFile(const QString &filename, const QString &realFilename, QIODevice *io, KisImageSP image); private: void writeString(QTextStream& out, int spacing, const QString& setting, const QString& value); void writeInt(QTextStream& out, int spacing, const QString& setting, int value); }; #endif diff --git a/plugins/impex/qml/qml_export.cc b/plugins/impex/qml/qml_export.cc index 8de5713de9..732b277aa7 100644 --- a/plugins/impex/qml/qml_export.cc +++ b/plugins/impex/qml/qml_export.cc @@ -1,67 +1,68 @@ /* * Copyright (c) 2013 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qml_export.h" #include #include #include #include #include #include #include #include #include "qml_converter.h" #include K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_qml_export.json", registerPlugin();) QMLExport::QMLExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } QMLExport::~QMLExport() { } KisImportExportErrorCode QMLExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/) { KisImageSP image = document->savingImage(); Q_CHECK_PTR(image); QMLConverter converter; return converter.buildFile(filename(), realFilename(), io, image); } void QMLExport::initializeCapabilities() { addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED)); QList > supportedColorModels; supportedColorModels << QPair() << QPair(RGBAColorModelID, Integer8BitsColorDepthID) << QPair(GrayAColorModelID, Integer8BitsColorDepthID); addSupportedColorModels(supportedColorModels, "QML"); } #include diff --git a/plugins/impex/qml/qml_export.h b/plugins/impex/qml/qml_export.h index 7ca4de2a36..e951a92843 100644 --- a/plugins/impex/qml/qml_export.h +++ b/plugins/impex/qml/qml_export.h @@ -1,36 +1,37 @@ /* * Copyright (c) 2013 Sven Langkamp * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _QML_EXPORT_H_ #define _QML_EXPORT_H_ #include #include class QMLExport : public KisImportExportFilter { Q_OBJECT public: QMLExport(QObject *parent, const QVariantList &); ~QMLExport() override; public: KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override; void initializeCapabilities() override; }; #endif diff --git a/plugins/paintops/libpaintop/kis_color_source.cpp b/plugins/paintops/libpaintop/kis_color_source.cpp index 56f49909f9..b4772ead99 100644 --- a/plugins/paintops/libpaintop/kis_color_source.cpp +++ b/plugins/paintops/libpaintop/kis_color_source.cpp @@ -1,296 +1,297 @@ /* * Copyright (c) 2006-2007, 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_source.h" #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include "kis_selection.h" #include #include #include KisColorSource::~KisColorSource() { } const KoColor black; const KoColor& KisColorSource::uniformColor() const { qFatal("Not an uniform color."); return black; } KisUniformColorSource::KisUniformColorSource() { } KisUniformColorSource::~KisUniformColorSource() { } void KisUniformColorSource::rotate(double) {} void KisUniformColorSource::resize(double , double) { // Do nothing as plain color does not have size } void KisUniformColorSource::colorize(KisPaintDeviceSP dev, const QRect& size, const QPoint&) const { Q_UNUSED(size); KoColor c(dev->colorSpace()); c.fromKoColor(m_color); dev->dataManager()->setDefaultPixel(c.data()); dev->clear(); } const KoColor& KisUniformColorSource::uniformColor() const { return m_color; } void KisUniformColorSource::applyColorTransformation(const KoColorTransformation* transfo) { transfo->transform(m_color.data(), m_color.data(), 1); } const KoColorSpace* KisUniformColorSource::colorSpace() const { return m_color.colorSpace(); } bool KisUniformColorSource::isUniformColor() const { return true; } //-------------------------------------------------// //---------------- KisPlainColorSource ---------------// //-------------------------------------------------// KisPlainColorSource::KisPlainColorSource(const KoColor& backGroundColor, const KoColor& foreGroundColor) : m_backGroundColor(backGroundColor) , m_cachedBackGroundColor(backGroundColor) , m_foreGroundColor(foreGroundColor) { } KisPlainColorSource::~KisPlainColorSource() { } void KisPlainColorSource::selectColor(double mix, const KisPaintInformation &pi) { Q_UNUSED(pi); if (m_color.colorSpace() != m_foreGroundColor.colorSpace()) { m_color = KoColor(m_foreGroundColor.colorSpace()); m_cachedBackGroundColor = KoColor(m_foreGroundColor.colorSpace()); m_cachedBackGroundColor.fromKoColor(m_backGroundColor); } const quint8 *colors[2]; colors[0] = m_cachedBackGroundColor.data(); colors[1] = m_foreGroundColor.data(); // equally distribute mix factor over [0..255] // mix * 256 ensures that, with exception of mix==1.0, which gets special handling const int weight = (mix == 1.0) ? 255 : (int)(mix * 256); const qint16 weights[2] = { (qint16)(255 - weight), (qint16)weight }; m_color.colorSpace()->mixColorsOp()->mixColors(colors, weights, 2, m_color.data()); } //-------------------------------------------------// //--------------- KisGradientColorSource -------------// //-------------------------------------------------// KisGradientColorSource::KisGradientColorSource(const KoAbstractGradientSP gradient, const KoColorSpace* workingCS) : m_gradient(gradient) { m_color = KoColor(workingCS); Q_ASSERT(gradient); } KisGradientColorSource::~KisGradientColorSource() { } void KisGradientColorSource::selectColor(double mix, const KisPaintInformation &pi) { Q_UNUSED(pi); if (m_gradient) { m_gradient->colorAt(m_color, mix); } } //-------------------------------------------------// //--------------- KisGradientColorSource -------------// //-------------------------------------------------// KisUniformRandomColorSource::KisUniformRandomColorSource() { } KisUniformRandomColorSource::~KisUniformRandomColorSource() { } void KisUniformRandomColorSource::selectColor(double mix, const KisPaintInformation &pi) { Q_UNUSED(pi); Q_UNUSED(mix); KisRandomSourceSP source = pi.randomSource(); m_color.fromQColor(QColor((int)source->generate(0, 255), (int)source->generate(0, 255), (int)source->generate(0, 255))); } //------------------------------------------------------// //--------------- KisTotalRandomColorSource ---------------// //------------------------------------------------------// KisTotalRandomColorSource::KisTotalRandomColorSource() : m_colorSpace(KoColorSpaceRegistry::instance()->rgb8()) { } KisTotalRandomColorSource::~KisTotalRandomColorSource() { } void KisTotalRandomColorSource::selectColor(double mix, const KisPaintInformation &pi) { Q_UNUSED(mix); Q_UNUSED(pi); } void KisTotalRandomColorSource::applyColorTransformation(const KoColorTransformation*) {} const KoColorSpace* KisTotalRandomColorSource::colorSpace() const { return m_colorSpace; } void KisTotalRandomColorSource::colorize(KisPaintDeviceSP dev, const QRect& rect, const QPoint&) const { KoColor kc(dev->colorSpace()); QColor qc; std::random_device rand_dev; std::default_random_engine rand_engine{rand_dev()}; std::uniform_int_distribution<> rand_distr(0, 255); int pixelSize = dev->colorSpace()->pixelSize(); KisHLineIteratorSP it = dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width()); for (int y = 0; y < rect.height(); y++) { do { qc.setRgb(rand_distr(rand_engine), rand_distr(rand_engine), rand_distr(rand_engine)); kc.fromQColor(qc); memcpy(it->rawData(), kc.data(), pixelSize); } while (it->nextPixel()); it->nextRow(); } } bool KisTotalRandomColorSource::isUniformColor() const { return false; } void KisTotalRandomColorSource::rotate(double) {} void KisTotalRandomColorSource::resize(double , double) {} KoPatternColorSource::KoPatternColorSource(KisPaintDeviceSP _pattern, int _width, int _height, bool _locked) : m_device(_pattern) , m_bounds(QRect(0, 0, _width, _height)) , m_locked(_locked) { } KoPatternColorSource::~KoPatternColorSource() { } void KoPatternColorSource::selectColor(double mix, const KisPaintInformation &pi) { Q_UNUSED(mix); Q_UNUSED(pi); } void KoPatternColorSource::applyColorTransformation(const KoColorTransformation* transfo) { Q_UNUSED(transfo); } const KoColorSpace* KoPatternColorSource::colorSpace() const { return m_device->colorSpace(); } void KoPatternColorSource::colorize(KisPaintDeviceSP device, const QRect& rect, const QPoint& offset) const { KisFillPainter painter(device); if (m_locked) { painter.fillRect(rect.x(), rect.y(), rect.width(), rect.height(), m_device, m_bounds); } else { int x = offset.x() % m_bounds.width(); int y = offset.y() % m_bounds.height(); // Change the position, because the pattern is always applied starting // from (0,0) in the paint device reference device->setX(x); device->setY(y); painter.fillRect(rect.x() + x, rect.y() + y, rect.width(), rect.height(), m_device, m_bounds); device->setX(0); device->setY(0); } } void KoPatternColorSource::rotate(double r) { Q_UNUSED(r); } void KoPatternColorSource::resize(double xs, double ys) { Q_UNUSED(xs); Q_UNUSED(ys); } bool KoPatternColorSource::isUniformColor() const { return false; } diff --git a/plugins/paintops/libpaintop/kis_color_source.h b/plugins/paintops/libpaintop/kis_color_source.h index 9db82b491a..961ec4eba4 100644 --- a/plugins/paintops/libpaintop/kis_color_source.h +++ b/plugins/paintops/libpaintop/kis_color_source.h @@ -1,152 +1,153 @@ /* * Copyright (c) 2006-2007, 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_DYNAMIC_COLORING_H_ #define _KIS_DYNAMIC_COLORING_H_ #include "kis_paintop_option.h" #include #include #include #include #include class KoColorTransformation; class KisPaintInformation; /** * A color source allow to abstract how a brush is colorized, * and to apply transformation. * * The first function to call is @ref selectColor , then any of the transformation. */ class PAINTOP_EXPORT KisColorSource { public: virtual ~KisColorSource(); public: /** * This is function is called to initialize the color that will be used for the dab. * @param mix is a parameter between 0.0 and 1.0 * @param pi paint information */ virtual void selectColor(double mix, const KisPaintInformation &pi) = 0; /** * Apply a color transformation on the selected color */ virtual void applyColorTransformation(const KoColorTransformation* transfo) = 0; virtual const KoColorSpace* colorSpace() const = 0; /** * Apply the color on a paint device */ virtual void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& _offset) const = 0; /** * @return true if the color is an uniform color */ virtual bool isUniformColor() const = 0; /** * @return the color if the color is uniformed */ virtual const KoColor& uniformColor() const; }; class PAINTOP_EXPORT KisUniformColorSource : public KisColorSource { public: KisUniformColorSource(); ~KisUniformColorSource() override; virtual void rotate(double); virtual void resize(double , double); void applyColorTransformation(const KoColorTransformation* transfo) override; const KoColorSpace* colorSpace() const override; void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& offset) const override; bool isUniformColor() const override; const KoColor& uniformColor() const override; protected: KoColor m_color; }; class PAINTOP_EXPORT KisPlainColorSource : public KisUniformColorSource { public: KisPlainColorSource(const KoColor& backGroundColor, const KoColor& foreGroundColor); ~KisPlainColorSource() override; void selectColor(double mix, const KisPaintInformation &pi) override; private: KoColor m_backGroundColor; KoColor m_cachedBackGroundColor; KoColor m_foreGroundColor; }; class PAINTOP_EXPORT KisGradientColorSource : public KisUniformColorSource { public: KisGradientColorSource(const KoAbstractGradientSP gradient, const KoColorSpace* workingCS); ~KisGradientColorSource() override; void selectColor(double mix, const KisPaintInformation &pi) override; private: const KoAbstractGradientSP m_gradient; }; class PAINTOP_EXPORT KisUniformRandomColorSource : public KisUniformColorSource { public: KisUniformRandomColorSource(); ~KisUniformRandomColorSource() override; void selectColor(double mix, const KisPaintInformation &pi) override; }; class PAINTOP_EXPORT KisTotalRandomColorSource : public KisColorSource { public: KisTotalRandomColorSource(); ~KisTotalRandomColorSource() override; public: void selectColor(double mix, const KisPaintInformation &pi) override; void applyColorTransformation(const KoColorTransformation* transfo) override; const KoColorSpace* colorSpace() const override; void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& offset) const override; virtual void rotate(double r); virtual void resize(double xs, double ys); bool isUniformColor() const override; private: const KoColorSpace* m_colorSpace; }; class PAINTOP_EXPORT KoPatternColorSource : public KisColorSource { public: KoPatternColorSource(KisPaintDeviceSP _pattern, int _width, int _height, bool _locked); ~KoPatternColorSource() override; public: void selectColor(double mix, const KisPaintInformation &pi) override; void applyColorTransformation(const KoColorTransformation* transfo) override; const KoColorSpace* colorSpace() const override; void colorize(KisPaintDeviceSP, const QRect& rect, const QPoint& _offset) const override; virtual void rotate(double r); virtual void resize(double xs, double ys); bool isUniformColor() const override; private: const KisPaintDeviceSP m_device; QRect m_bounds; bool m_locked; }; #endif diff --git a/plugins/paintops/libpaintop/kis_curve_label.h b/plugins/paintops/libpaintop/kis_curve_label.h index 97168acbb8..be52b620fe 100644 --- a/plugins/paintops/libpaintop/kis_curve_label.h +++ b/plugins/paintops/libpaintop/kis_curve_label.h @@ -1,44 +1,45 @@ /* * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CURVE_LABEL_H_ #define _KIS_CURVE_LABEL_H_ #include class QString; class QImage; struct PAINTOP_EXPORT KisCurveLabel { public: KisCurveLabel(); KisCurveLabel(const QString&); KisCurveLabel(const QImage&); KisCurveLabel(const KisCurveLabel&); KisCurveLabel& operator=(const KisCurveLabel&); ~KisCurveLabel(); QString name() const; QImage icon() const; private: struct Private; Private* const d; }; #endif diff --git a/plugins/paintops/libpaintop/kis_dynamic_sensor.cc b/plugins/paintops/libpaintop/kis_dynamic_sensor.cc index 8f31aa9a1a..0c56495f77 100644 --- a/plugins/paintops/libpaintop/kis_dynamic_sensor.cc +++ b/plugins/paintops/libpaintop/kis_dynamic_sensor.cc @@ -1,575 +1,576 @@ /* * Copyright (c) 2007 Cyrille Berger * Copyright (c) 2011 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dynamic_sensor.h" #include #include "kis_algebra_2d.h" #include "sensors/kis_dynamic_sensors.h" #include "sensors/kis_dynamic_sensor_distance.h" #include "sensors/kis_dynamic_sensor_drawing_angle.h" #include "sensors/kis_dynamic_sensor_time.h" #include "sensors/kis_dynamic_sensor_fade.h" #include "sensors/kis_dynamic_sensor_fuzzy.h" KisDynamicSensor::KisDynamicSensor(DynamicSensorType type) : m_length(-1) , m_type(type) , m_customCurve(false) , m_active(false) { } KisDynamicSensor::~KisDynamicSensor() { } QWidget* KisDynamicSensor::createConfigurationWidget(QWidget* parent, QWidget*) { Q_UNUSED(parent); return 0; } void KisDynamicSensor::reset() { } KisDynamicSensorSP KisDynamicSensor::id2Sensor(const KoID& id, const QString &parentOptionName) { if (id.id() == PressureId.id()) { return new KisDynamicSensorPressure(); } else if (id.id() == PressureInId.id()) { return new KisDynamicSensorPressureIn(); } else if (id.id() == XTiltId.id()) { return new KisDynamicSensorXTilt(); } else if (id.id() == YTiltId.id()) { return new KisDynamicSensorYTilt(); } else if (id.id() == TiltDirectionId.id()) { return new KisDynamicSensorTiltDirection(); } else if (id.id() == TiltElevationId.id()) { return new KisDynamicSensorTiltElevation(); } else if (id.id() == SpeedId.id()) { return new KisDynamicSensorSpeed(); } else if (id.id() == DrawingAngleId.id()) { return new KisDynamicSensorDrawingAngle(); } else if (id.id() == RotationId.id()) { return new KisDynamicSensorRotation(); } else if (id.id() == DistanceId.id()) { return new KisDynamicSensorDistance(); } else if (id.id() == TimeId.id()) { return new KisDynamicSensorTime(); } else if (id.id() == FuzzyPerDabId.id()) { return new KisDynamicSensorFuzzy(false, parentOptionName); } else if (id.id() == FuzzyPerStrokeId.id()) { return new KisDynamicSensorFuzzy(true, parentOptionName); } else if (id.id() == FadeId.id()) { return new KisDynamicSensorFade(); } else if (id.id() == PerspectiveId.id()) { return new KisDynamicSensorPerspective(); } else if (id.id() == TangentialPressureId.id()) { return new KisDynamicSensorTangentialPressure(); } dbgPlugins << "Unknown transform parameter :" << id.id(); return 0; } DynamicSensorType KisDynamicSensor::id2Type(const KoID &id) { if (id.id() == PressureId.id()) { return PRESSURE; } else if (id.id() == PressureInId.id()) { return PRESSURE_IN; } else if (id.id() == XTiltId.id()) { return XTILT; } else if (id.id() == YTiltId.id()) { return YTILT; } else if (id.id() == TiltDirectionId.id()) { return TILT_DIRECTION; } else if (id.id() == TiltElevationId.id()) { return TILT_ELEVATATION; } else if (id.id() == SpeedId.id()) { return SPEED; } else if (id.id() == DrawingAngleId.id()) { return ANGLE; } else if (id.id() == RotationId.id()) { return ROTATION; } else if (id.id() == DistanceId.id()) { return DISTANCE; } else if (id.id() == TimeId.id()) { return TIME; } else if (id.id() == FuzzyPerDabId.id()) { return FUZZY_PER_DAB; } else if (id.id() == FuzzyPerStrokeId.id()) { return FUZZY_PER_STROKE; } else if (id.id() == FadeId.id()) { return FADE; } else if (id.id() == PerspectiveId.id()) { return PERSPECTIVE; } else if (id.id() == TangentialPressureId.id()) { return TANGENTIAL_PRESSURE; } return UNKNOWN; } KisDynamicSensorSP KisDynamicSensor::type2Sensor(DynamicSensorType sensorType, const QString &parentOptionName) { switch (sensorType) { case FUZZY_PER_DAB: return new KisDynamicSensorFuzzy(false, parentOptionName); case FUZZY_PER_STROKE: return new KisDynamicSensorFuzzy(true, parentOptionName); case SPEED: return new KisDynamicSensorSpeed(); case FADE: return new KisDynamicSensorFade(); case DISTANCE: return new KisDynamicSensorDistance(); case TIME: return new KisDynamicSensorTime(); case ANGLE: return new KisDynamicSensorDrawingAngle(); case ROTATION: return new KisDynamicSensorRotation(); case PRESSURE: return new KisDynamicSensorPressure(); case XTILT: return new KisDynamicSensorXTilt(); case YTILT: return new KisDynamicSensorYTilt(); case TILT_DIRECTION: return new KisDynamicSensorTiltDirection(); case TILT_ELEVATATION: return new KisDynamicSensorTiltElevation(); case PERSPECTIVE: return new KisDynamicSensorPerspective(); case TANGENTIAL_PRESSURE: return new KisDynamicSensorTangentialPressure(); case PRESSURE_IN: return new KisDynamicSensorPressureIn(); default: return 0; } } QString KisDynamicSensor::minimumLabel(DynamicSensorType sensorType) { switch (sensorType) { case FUZZY_PER_DAB: case FUZZY_PER_STROKE: return QString(); case FADE: return i18n("0"); case DISTANCE: return i18n("0 px"); case TIME: return i18n("0 s"); case ANGLE: return i18n("0°"); case SPEED: return i18n("Slow"); case ROTATION: return i18n("0°"); case PRESSURE: return i18n("Low"); case XTILT: return i18n("-30°"); case YTILT: return i18n("-30°"); case TILT_DIRECTION: return i18n("0°"); case TILT_ELEVATATION: return i18n("90°"); case PERSPECTIVE: return i18n("Far"); case TANGENTIAL_PRESSURE: case PRESSURE_IN: return i18n("Low"); default: return i18n("0.0"); } } QString KisDynamicSensor::maximumLabel(DynamicSensorType sensorType, int max) { switch (sensorType) { case FUZZY_PER_DAB: case FUZZY_PER_STROKE: return QString(); case FADE: if (max < 0) return i18n("1000"); else return i18n("%1", max); case DISTANCE: if (max < 0) return i18n("30 px"); else return i18n("%1 px", max); case TIME: if (max < 0) return i18n("3 s"); else return i18n("%1 s", max / 1000); case ANGLE: return i18n("360°"); case SPEED: return i18n("Fast"); case ROTATION: return i18n("360°"); case PRESSURE: return i18n("High"); case XTILT: return i18n("30°"); case YTILT: return i18n("30°"); case TILT_DIRECTION: return i18n("360°"); case TILT_ELEVATATION: return i18n("0°"); case PERSPECTIVE: return i18n("Near"); case TANGENTIAL_PRESSURE: case PRESSURE_IN: return i18n("High"); default: return i18n("1.0"); }; } int KisDynamicSensor::minimumValue(DynamicSensorType sensorType) { switch (sensorType) { case FUZZY_PER_DAB: case FUZZY_PER_STROKE: case FADE: case DISTANCE: case TIME: case ANGLE: case SPEED: case ROTATION: case PRESSURE: case TILT_DIRECTION: case PERSPECTIVE: case PRESSURE_IN: return 0; case XTILT: case YTILT: return -30; case TILT_ELEVATATION: return 90; case TANGENTIAL_PRESSURE: default: return 0; } } int KisDynamicSensor::maximumValue(DynamicSensorType sensorType, int max) { switch (sensorType) { case FUZZY_PER_DAB: case FUZZY_PER_STROKE: case SPEED: case PERSPECTIVE: case TANGENTIAL_PRESSURE: case PRESSURE_IN: case PRESSURE: return 100; case FADE: if (max < 0) { return 1000; } else { return max; } case DISTANCE: if (max < 0) { return 30; } else { return max; } case TIME: if (max < 0) { return 3000; } else { return max; } case ANGLE: case ROTATION: case TILT_DIRECTION: return 360; case XTILT: case YTILT: return 30; case TILT_ELEVATATION: return 0; default: return 100; }; } QString KisDynamicSensor::valueSuffix(DynamicSensorType sensorType) { switch (sensorType) { case FUZZY_PER_DAB: case FUZZY_PER_STROKE: case SPEED: case PRESSURE: case PERSPECTIVE: case TANGENTIAL_PRESSURE: case PRESSURE_IN: return i18n("%"); case FADE: return QString(); case DISTANCE: return i18n(" px"); case TIME: return i18n(" ms"); case ANGLE: case ROTATION: case XTILT: case YTILT: case TILT_DIRECTION: case TILT_ELEVATATION: return i18n("°"); default: return i18n("%"); }; } KisDynamicSensorSP KisDynamicSensor::createFromXML(const QString& s, const QString &parentOptionName) { QDomDocument doc; doc.setContent(s); QDomElement e = doc.documentElement(); return createFromXML(e, parentOptionName); } KisDynamicSensorSP KisDynamicSensor::createFromXML(const QDomElement& e, const QString &parentOptionName) { QString id = e.attribute("id", ""); KisDynamicSensorSP sensor = id2Sensor(id, parentOptionName); if (sensor) { sensor->fromXML(e); } return sensor; } QList KisDynamicSensor::sensorsIds() { QList ids; ids << PressureId << PressureInId << XTiltId << YTiltId << TiltDirectionId << TiltElevationId << SpeedId << DrawingAngleId << RotationId << DistanceId << TimeId << FuzzyPerDabId << FuzzyPerStrokeId << FadeId << PerspectiveId << TangentialPressureId; return ids; } QList KisDynamicSensor::sensorsTypes() { QList sensorTypes; sensorTypes << PRESSURE << PRESSURE_IN << XTILT << YTILT << TILT_DIRECTION << TILT_ELEVATATION << SPEED << ANGLE << ROTATION << DISTANCE << TIME << FUZZY_PER_DAB << FUZZY_PER_STROKE << FADE << PERSPECTIVE << TANGENTIAL_PRESSURE; return sensorTypes; } QString KisDynamicSensor::id(DynamicSensorType sensorType) { switch (sensorType) { case FUZZY_PER_DAB: return "fuzzy"; case FUZZY_PER_STROKE: return "fuzzystroke"; case FADE: return "fade"; case DISTANCE: return "distance"; case TIME: return "time"; case ANGLE: return "drawingangle"; case SPEED: return "speed"; case ROTATION: return "rotation"; case PRESSURE: return "pressure"; case XTILT: return "xtilt"; case YTILT: return "ytilt"; case TILT_DIRECTION: return "ascension"; case TILT_ELEVATATION: return "declination"; case PERSPECTIVE: return "perspective"; case TANGENTIAL_PRESSURE: return "tangentialpressure"; case PRESSURE_IN: return "pressurein"; case SENSORS_LIST: return "sensorslist"; default: return QString(); }; } void KisDynamicSensor::toXML(QDomDocument& doc, QDomElement& elt) const { elt.setAttribute("id", id(sensorType())); if (m_customCurve) { QDomElement curve_elt = doc.createElement("curve"); QDomText text = doc.createTextNode(m_curve.toString()); curve_elt.appendChild(text); elt.appendChild(curve_elt); } } void KisDynamicSensor::fromXML(const QDomElement& e) { Q_ASSERT(e.attribute("id", "") == id(sensorType())); m_customCurve = false; QDomElement curve_elt = e.firstChildElement("curve"); if (!curve_elt.isNull()) { m_customCurve = true; m_curve.fromString(curve_elt.text()); } } qreal KisDynamicSensor::parameter(const KisPaintInformation& info) { const qreal val = value(info); if (m_customCurve) { qreal scaledVal = isAdditive() ? additiveToScaling(val) : val; const QVector transfer = m_curve.floatTransfer(256); scaledVal = KisCubicCurve::interpolateLinear(scaledVal, transfer); return isAdditive() ? scalingToAdditive(scaledVal) : scaledVal; } else { return val; } } void KisDynamicSensor::setCurve(const KisCubicCurve& curve) { m_customCurve = true; m_curve = curve; } const KisCubicCurve& KisDynamicSensor::curve() const { return m_curve; } void KisDynamicSensor::removeCurve() { m_customCurve = false; } bool KisDynamicSensor::hasCustomCurve() const { return m_customCurve; } bool KisDynamicSensor::dependsOnCanvasRotation() const { return true; } bool KisDynamicSensor::isAdditive() const { return false; } bool KisDynamicSensor::isAbsoluteRotation() const { return false; } void KisDynamicSensor::setActive(bool active) { m_active = active; } bool KisDynamicSensor::isActive() const { return m_active; } diff --git a/plugins/paintops/libpaintop/kis_dynamic_sensor.h b/plugins/paintops/libpaintop/kis_dynamic_sensor.h index a4b843c36f..b349822eda 100644 --- a/plugins/paintops/libpaintop/kis_dynamic_sensor.h +++ b/plugins/paintops/libpaintop/kis_dynamic_sensor.h @@ -1,221 +1,222 @@ /* * Copyright (c) 2006 Cyrille Berger * Copyright (c) 2011 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_DYNAMIC_SENSOR_H_ #define _KIS_DYNAMIC_SENSOR_H_ #include #include #include #include #include "kis_serializable_configuration.h" #include "kis_curve_label.h" #include #include #include class QWidget; class KisPaintInformation; const KoID FuzzyPerDabId("fuzzy", ki18n("Fuzzy Dab")); ///< generate a random number const KoID FuzzyPerStrokeId("fuzzystroke", ki18n("Fuzzy Stroke")); ///< generate a random number const KoID SpeedId("speed", ki18n("Speed")); ///< generate a number depending on the speed of the cursor const KoID FadeId("fade", ki18n("Fade")); ///< generate a number that increase every time you call it (e.g. per dab) const KoID DistanceId("distance", ki18n("Distance")); ///< generate a number that increase with distance const KoID TimeId("time", ki18n("Time")); ///< generate a number that increase with time const KoID DrawingAngleId("drawingangle", ki18n("Drawing angle")); ///< number depending on the angle const KoID RotationId("rotation", ki18n("Rotation")); ///< rotation coming from the device const KoID PressureId("pressure", ki18n("Pressure")); ///< number depending on the pressure const KoID PressureInId("pressurein", ki18n("PressureIn")); ///< number depending on the pressure const KoID XTiltId("xtilt", ki18n("X-Tilt")); ///< number depending on X-tilt const KoID YTiltId("ytilt", ki18n("Y-Tilt")); ///< number depending on Y-tilt /** * "TiltDirection" and "TiltElevation" parameters are written to * preset files as "ascension" and "declination" to keep backward * compatibility with older presets from the days when they were called * differently. */ const KoID TiltDirectionId("ascension", ki18n("Tilt direction")); /// < number depending on the X and Y tilt, tilt direction is 0 when stylus nib points to you and changes clockwise from -180 to +180. const KoID TiltElevationId("declination", ki18n("Tilt elevation")); /// < tilt elevation is 90 when stylus is perpendicular to tablet and 0 when it's parallel to tablet const KoID PerspectiveId("perspective", ki18n("Perspective")); ///< number depending on the distance on the perspective grid const KoID TangentialPressureId("tangentialpressure", ki18n("Tangential pressure")); ///< the wheel on an airbrush device const KoID SensorsListId("sensorslist", "SHOULD NOT APPEAR IN THE UI !"); ///< this a non user-visible sensor that can store a list of other sensors, and multiply their output class KisDynamicSensor; typedef KisSharedPtr KisDynamicSensorSP; enum DynamicSensorType { FUZZY_PER_DAB, FUZZY_PER_STROKE, SPEED, FADE, DISTANCE, TIME, ANGLE, ROTATION, PRESSURE, XTILT, YTILT, TILT_DIRECTION, TILT_ELEVATATION, PERSPECTIVE, TANGENTIAL_PRESSURE, SENSORS_LIST, PRESSURE_IN, UNKNOWN = 255 }; /** * Sensors are used to extract from KisPaintInformation a single * double value which can be used to control the parameters of * a brush. */ class PAINTOP_EXPORT KisDynamicSensor : public KisSerializableConfiguration { public: enum ParameterSign { NegativeParameter = -1, UnSignedParameter = 0, PositiveParameter = 1 }; protected: KisDynamicSensor(DynamicSensorType type); public: ~KisDynamicSensor() override; /** * @return the value of this sensor for the given KisPaintInformation */ qreal parameter(const KisPaintInformation& info); /** * This function is call before beginning a stroke to reset the sensor. * Default implementation does nothing. */ virtual void reset(); /** * @param parent the parent QWidget * @param selector is a \ref QWidget that contains a signal called "parametersChanged()" */ virtual QWidget* createConfigurationWidget(QWidget* parent, QWidget* selector); /** * Creates a sensor from its identifier. */ static KisDynamicSensorSP id2Sensor(const KoID& id, const QString &parentOptionName); static KisDynamicSensorSP id2Sensor(const QString& s, const QString &parentOptionName) { return id2Sensor(KoID(s), parentOptionName); } static DynamicSensorType id2Type(const KoID& id); static DynamicSensorType id2Type(const QString& s) { return id2Type(KoID(s)); } /** * type2Sensor creates a new sensor for the give type */ static KisDynamicSensorSP type2Sensor(DynamicSensorType sensorType, const QString &parentOptionName); static QString minimumLabel(DynamicSensorType sensorType); static QString maximumLabel(DynamicSensorType sensorType, int max = -1); static int minimumValue(DynamicSensorType sensorType); static int maximumValue(DynamicSensorType sensorType, int max = -1); static QString valueSuffix(DynamicSensorType sensorType); static KisDynamicSensorSP createFromXML(const QString&, const QString &parentOptionName); static KisDynamicSensorSP createFromXML(const QDomElement&, const QString &parentOptionName); /** * @return the list of sensors */ static QList sensorsIds(); static QList sensorsTypes(); /** * @return the identifier of this sensor */ static QString id(DynamicSensorType sensorType); using KisSerializableConfiguration::fromXML; using KisSerializableConfiguration::toXML; void toXML(QDomDocument&, QDomElement&) const override; void fromXML(const QDomElement&) override; void setCurve(const KisCubicCurve& curve); const KisCubicCurve& curve() const; void removeCurve(); bool hasCustomCurve() const; void setActive(bool active); bool isActive() const; virtual bool dependsOnCanvasRotation() const; virtual bool isAdditive() const; virtual bool isAbsoluteRotation() const; inline DynamicSensorType sensorType() const { return m_type; } /** * @return the currently set length or -1 if not relevant */ int length() { return m_length; } public: static inline qreal scalingToAdditive(qreal x) { return -1.0 + 2.0 * x; } static inline qreal additiveToScaling(qreal x) { return 0.5 * (1.0 + x); } protected: virtual qreal value(const KisPaintInformation& info) = 0; int m_length; private: Q_DISABLE_COPY(KisDynamicSensor) DynamicSensorType m_type; bool m_customCurve; KisCubicCurve m_curve; bool m_active; }; #endif diff --git a/plugins/paintops/libpaintop/kis_dynamic_sensors.h b/plugins/paintops/libpaintop/kis_dynamic_sensors.h index 34dc12a7f0..2bd15cc820 100644 --- a/plugins/paintops/libpaintop/kis_dynamic_sensors.h +++ b/plugins/paintops/libpaintop/kis_dynamic_sensors.h @@ -1,140 +1,141 @@ /* * Copyright (c) 2006-2007,2010 Cyrille Berger * Copyright (c) 2011 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_DYNAMIC_SENSORS_H_ #define _KIS_DYNAMIC_SENSORS_H_ #include "../kis_dynamic_sensor.h" #include "kis_paint_information.h" class KisDynamicSensorSpeed : public KisDynamicSensor { public: KisDynamicSensorSpeed(); virtual ~KisDynamicSensorSpeed() { } virtual qreal value(const KisPaintInformation& info); void reset() { m_speed = -1.0; } private: double m_speed; }; class KisDynamicSensorRotation : public KisDynamicSensor { public: KisDynamicSensorRotation(); virtual ~KisDynamicSensorRotation() { } virtual qreal value(const KisPaintInformation& info) { return info.rotation() / 360.0; } }; class KisDynamicSensorPressure : public KisDynamicSensor { public: KisDynamicSensorPressure(); virtual ~KisDynamicSensorPressure() { } virtual qreal value(const KisPaintInformation& info) { return info.pressure(); } }; class KisDynamicSensorPressureIn : public KisDynamicSensor { public: KisDynamicSensorPressureIn(); virtual ~KisDynamicSensorPressureIn() { } virtual qreal value(const KisPaintInformation& info) { if(!info.isHoveringMode()) { if(info.pressure() > lastPressure) { lastPressure = info.pressure(); } return lastPressure; } lastPressure = 0.0; return 0.0; } private: qreal lastPressure; }; class KisDynamicSensorXTilt : public KisDynamicSensor { public: KisDynamicSensorXTilt(); virtual ~KisDynamicSensorXTilt() { } virtual qreal value(const KisPaintInformation& info) { return 1.0 - fabs(info.xTilt()) / 60.0; } }; class KisDynamicSensorYTilt : public KisDynamicSensor { public: KisDynamicSensorYTilt(); virtual ~KisDynamicSensorYTilt() { } virtual qreal value(const KisPaintInformation& info) { return 1.0 - fabs(info.yTilt()) / 60.0; } }; class KisDynamicSensorTiltDirection : public KisDynamicSensor { public: KisDynamicSensorTiltDirection(); virtual ~KisDynamicSensorTiltDirection() {} virtual qreal value(const KisPaintInformation& info) { return KisPaintInformation::tiltDirection(info, true); } }; class KisDynamicSensorTiltElevation : public KisDynamicSensor { public: KisDynamicSensorTiltElevation(); virtual ~KisDynamicSensorTiltElevation() {} virtual qreal value(const KisPaintInformation& info) { return KisPaintInformation::tiltElevation(info, 60.0, 60.0, true); } }; class KisDynamicSensorPerspective : public KisDynamicSensor { public: KisDynamicSensorPerspective(); virtual ~KisDynamicSensorPerspective() { } virtual qreal value(const KisPaintInformation& info) { return info.perspective(); } }; class KisDynamicSensorTangentialPressure : public KisDynamicSensor { public: KisDynamicSensorTangentialPressure(); virtual ~KisDynamicSensorTangentialPressure() { } virtual qreal value(const KisPaintInformation& info) { return info.tangentialPressure(); } }; #endif diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.cc b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.cc index 02347e68dc..25af0e2aa0 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.cc +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.cc @@ -1,84 +1,85 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dynamic_sensor_distance.h" #include #include #include "ui_SensorDistanceConfiguration.h" #include KisDynamicSensorDistance::KisDynamicSensorDistance() : KisDynamicSensor(DISTANCE) , m_periodic(true) { setLength(30); } qreal KisDynamicSensorDistance::value(const KisPaintInformation& pi) { if (pi.isHoveringMode()) return 1.0; const qreal distance = m_periodic ? fmod(pi.totalStrokeLength(), m_length) : qMin(pi.totalStrokeLength(), (qreal)m_length); return distance / m_length; } void KisDynamicSensorDistance::setPeriodic(bool periodic) { m_periodic = periodic; } void KisDynamicSensorDistance::setLength(int length) { m_length = length; } QWidget* KisDynamicSensorDistance::createConfigurationWidget(QWidget* parent, QWidget* ss) { QWidget* wdg = new QWidget(parent); Ui_SensorDistanceConfiguration stc; stc.setupUi(wdg); stc.checkBoxRepeat->setChecked(m_periodic); connect(stc.checkBoxRepeat, SIGNAL(toggled(bool)), SLOT(setPeriodic(bool))); connect(stc.checkBoxRepeat, SIGNAL(toggled(bool)), ss, SIGNAL(parametersChanged())); stc.spinBoxLength->setValue(m_length); connect(stc.spinBoxLength, SIGNAL(valueChanged(int)), SLOT(setLength(int))); connect(stc.spinBoxLength, SIGNAL(valueChanged(int)), ss, SIGNAL(parametersChanged())); return wdg; } void KisDynamicSensorDistance::toXML(QDomDocument& doc, QDomElement& e) const { KisDynamicSensor::toXML(doc, e); e.setAttribute("periodic", m_periodic); e.setAttribute("length", m_length); } void KisDynamicSensorDistance::fromXML(const QDomElement& e) { KisDynamicSensor::fromXML(e); m_periodic = e.attribute("periodic", "0").toInt(); m_length = e.attribute("length", "30").toInt(); } diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.h b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.h index 7cc1b7093a..b63c0c9f9a 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.h +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_distance.h @@ -1,45 +1,46 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_DYNAMIC_SENSOR_DISTANCE_H_ #define _KIS_DYNAMIC_SENSOR_DISTANCE_H_ #include "kis_dynamic_sensor.h" // class KisDynamicSensorDistance : public QObject, public KisDynamicSensor { Q_OBJECT public: using KisSerializableConfiguration::fromXML; using KisSerializableConfiguration::toXML; KisDynamicSensorDistance(); ~KisDynamicSensorDistance() override { } qreal value(const KisPaintInformation&) override; QWidget* createConfigurationWidget(QWidget* parent, QWidget*) override; public Q_SLOTS: virtual void setPeriodic(bool periodic); virtual void setLength(int length); void toXML(QDomDocument&, QDomElement&) const override; void fromXML(const QDomElement&) override; private: bool m_periodic; }; #endif diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.cpp b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.cpp index 8eeff9ae21..b3eb6f119b 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.cpp +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.cpp @@ -1,89 +1,90 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dynamic_sensor_fade.h" #include #include #include "ui_SensorFadeConfiguration.h" #include static const int DEFAULT_LENGTH = 1000; KisDynamicSensorFade::KisDynamicSensorFade() : KisDynamicSensor(FADE) , m_periodic(false) { setLength(DEFAULT_LENGTH); } qreal KisDynamicSensorFade::value(const KisPaintInformation& pi) { if (pi.isHoveringMode()) return 1.0; const int currentValue = m_periodic ? pi.currentDabSeqNo() % m_length : qMin(pi.currentDabSeqNo(), m_length); return qreal(currentValue) / m_length; } void KisDynamicSensorFade::setPeriodic(bool periodic) { m_periodic = periodic; } void KisDynamicSensorFade::setLength(int length) { m_length = length; } QWidget* KisDynamicSensorFade::createConfigurationWidget(QWidget* parent, QWidget* ss) { QWidget* wdg = new QWidget(parent); Ui_SensorFadeConfiguration stc; stc.setupUi(wdg); stc.checkBoxRepeat->setChecked(m_periodic); stc.spinBoxLength->setSuffix(i18n(" px")); stc.spinBoxLength->setExponentRatio(3.0); connect(stc.checkBoxRepeat, SIGNAL(toggled(bool)), SLOT(setPeriodic(bool))); connect(stc.checkBoxRepeat, SIGNAL(toggled(bool)), ss, SIGNAL(parametersChanged())); stc.spinBoxLength->setValue(m_length); connect(stc.spinBoxLength, SIGNAL(valueChanged(int)), SLOT(setLength(int))); connect(stc.spinBoxLength, SIGNAL(valueChanged(int)), ss, SIGNAL(parametersChanged())); return wdg; } void KisDynamicSensorFade::toXML(QDomDocument& doc, QDomElement& e) const { KisDynamicSensor::toXML(doc, e); e.setAttribute("periodic", m_periodic); e.setAttribute("length", m_length); } void KisDynamicSensorFade::fromXML(const QDomElement& e) { KisDynamicSensor::fromXML(e); m_periodic = e.attribute("periodic", "0").toInt(); m_length = e.attribute("length", QString::number(DEFAULT_LENGTH)).toInt(); } diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.h b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.h index 4384b93d89..3c5d5af6e2 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.h +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_fade.h @@ -1,46 +1,47 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_DYNAMIC_SENSOR_FADE_H_ #define _KIS_DYNAMIC_SENSOR_FADE_H_ #include "kis_dynamic_sensor.h" class KisDynamicSensorFade : public QObject, public KisDynamicSensor { Q_OBJECT public: using KisSerializableConfiguration::fromXML; using KisSerializableConfiguration::toXML; KisDynamicSensorFade(); ~KisDynamicSensorFade() override { } qreal value(const KisPaintInformation&) override; QWidget* createConfigurationWidget(QWidget* parent, QWidget*) override; public Q_SLOTS: virtual void setPeriodic(bool periodic); virtual void setLength(int length); void toXML(QDomDocument&, QDomElement&) const override; void fromXML(const QDomElement&) override; private: bool m_periodic; }; #endif diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.cc b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.cc index 9c9a4d9fad..ec1385b7da 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.cc +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.cc @@ -1,94 +1,95 @@ /* * Copyright (c) 2007,2010 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dynamic_sensor_time.h" #include #include #include "ui_SensorTimeConfiguration.h" #include KisDynamicSensorTime::KisDynamicSensorTime() : KisDynamicSensor(TIME) , m_periodic(true) { setLength(3); } qreal KisDynamicSensorTime::value(const KisPaintInformation& pi) { if (pi.isHoveringMode()) return 1.0; const qreal currentTime = m_periodic ? std::fmod(pi.currentTime(), m_length) : qMin(pi.currentTime(), qreal(m_length)); return currentTime / qreal(m_length); } void KisDynamicSensorTime::reset() { } void KisDynamicSensorTime::setPeriodic(bool periodic) { m_periodic = periodic; } void KisDynamicSensorTime::setLength(qreal length) { m_length = (int)(length * 1000); // convert to milliseconds } QWidget* KisDynamicSensorTime::createConfigurationWidget(QWidget* parent, QWidget* ss) { QWidget* wdg = new QWidget(parent); Ui_SensorTimeConfiguration stc; stc.setupUi(wdg); stc.checkBoxRepeat->setChecked(m_periodic); connect(stc.checkBoxRepeat, SIGNAL(toggled(bool)), SLOT(setPeriodic(bool))); connect(stc.checkBoxRepeat, SIGNAL(toggled(bool)), ss, SIGNAL(parametersChanged())); stc.spinBoxDuration->setRange(0.02, 10.0, 2); stc.spinBoxDuration->setSuffix(i18n(" s")); stc.spinBoxDuration->setValue(m_length / 1000); connect(stc.spinBoxDuration, SIGNAL(valueChanged(qreal)), SLOT(setLength(qreal))); connect(stc.spinBoxDuration, SIGNAL(valueChanged(qreal)), ss, SIGNAL(parametersChanged())); return wdg; } void KisDynamicSensorTime::toXML(QDomDocument& doc, QDomElement& e) const { KisDynamicSensor::toXML(doc, e); e.setAttribute("periodic", m_periodic); e.setAttribute("duration", m_length); } void KisDynamicSensorTime::fromXML(const QDomElement& e) { KisDynamicSensor::fromXML(e); m_periodic = e.attribute("periodic", "0").toInt(); m_length = e.attribute("duration", "30").toInt(); } diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.h b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.h index 82e458cdc0..5a982dc532 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.h +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensor_time.h @@ -1,47 +1,48 @@ /* * Copyright (c) 2006 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_DYNAMIC_SENSOR_TIME_H_ #define _KIS_DYNAMIC_SENSOR_TIME_H_ #include "kis_dynamic_sensor.h" // class KisDynamicSensorTime : public QObject, public KisDynamicSensor { Q_OBJECT public: using KisSerializableConfiguration::fromXML; using KisSerializableConfiguration::toXML; KisDynamicSensorTime(); ~KisDynamicSensorTime() override { } qreal value(const KisPaintInformation&) override; void reset() override; QWidget* createConfigurationWidget(QWidget* parent, QWidget*) override; public Q_SLOTS: virtual void setPeriodic(bool periodic); virtual void setLength(qreal length); void toXML(QDomDocument&, QDomElement&) const override; void fromXML(const QDomElement&) override; private: bool m_periodic; }; #endif diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc index f29e6c09b4..c871947bc4 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc @@ -1,96 +1,97 @@ /* * Copyright (c) 2007,2010 Cyrille Berger * Copyright (c) 2011 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dynamic_sensors.h" KisDynamicSensorSpeed::KisDynamicSensorSpeed() : KisDynamicSensor(SPEED) { } qreal KisDynamicSensorSpeed::value(const KisPaintInformation& info) { /** * The value of maximum speed was measured empirically. This is * the speed that is quite easy to get with an A6 tablet and quite * a big image. If you need smaller speeds, just change the curve. */ const qreal maxSpeed = 30.0; // px / ms const qreal blendExponent = 0.05; qreal currentSpeed = info.drawingSpeed() / maxSpeed; if (m_speed >= 0.0) { m_speed = qMin(1.0, (m_speed * (1 - blendExponent) + currentSpeed * blendExponent)); } else { m_speed = currentSpeed; } return m_speed; } KisDynamicSensorRotation::KisDynamicSensorRotation() : KisDynamicSensor(ROTATION) { } KisDynamicSensorPressure::KisDynamicSensorPressure() : KisDynamicSensor(PRESSURE) { } KisDynamicSensorPressureIn::KisDynamicSensorPressureIn() : KisDynamicSensor(PRESSURE_IN) , lastPressure(0.0) { } KisDynamicSensorXTilt::KisDynamicSensorXTilt() : KisDynamicSensor(XTILT) { } KisDynamicSensorYTilt::KisDynamicSensorYTilt() : KisDynamicSensor(YTILT) { } KisDynamicSensorTiltDirection::KisDynamicSensorTiltDirection() : KisDynamicSensor(TILT_DIRECTION) { } KisDynamicSensorTiltElevation::KisDynamicSensorTiltElevation() : KisDynamicSensor(TILT_ELEVATATION) { } KisDynamicSensorPerspective::KisDynamicSensorPerspective() : KisDynamicSensor(PERSPECTIVE) { } KisDynamicSensorTangentialPressure::KisDynamicSensorTangentialPressure() : KisDynamicSensor(TANGENTIAL_PRESSURE) { } diff --git a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.h b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.h index 1d21d68fb0..e2c05c7e94 100644 --- a/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.h +++ b/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.h @@ -1,140 +1,141 @@ /* * Copyright (c) 2006-2007,2010 Cyrille Berger * Copyright (c) 2011 Lukáš Tvrdý * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_DYNAMIC_SENSORS_H_ #define _KIS_DYNAMIC_SENSORS_H_ #include "../kis_dynamic_sensor.h" #include class KisDynamicSensorSpeed : public KisDynamicSensor { public: KisDynamicSensorSpeed(); ~KisDynamicSensorSpeed() override { } qreal value(const KisPaintInformation& info) override; void reset() override { m_speed = -1.0; } private: double m_speed; }; class KisDynamicSensorRotation : public KisDynamicSensor { public: KisDynamicSensorRotation(); ~KisDynamicSensorRotation() override { } qreal value(const KisPaintInformation& info) override { return info.rotation() / 360.0; } }; class KisDynamicSensorPressure : public KisDynamicSensor { public: KisDynamicSensorPressure(); ~KisDynamicSensorPressure() override { } qreal value(const KisPaintInformation& info) override { return info.pressure(); } }; class KisDynamicSensorPressureIn : public KisDynamicSensor { public: KisDynamicSensorPressureIn(); ~KisDynamicSensorPressureIn() override { } qreal value(const KisPaintInformation& info) override { if(!info.isHoveringMode()) { if(info.pressure() > lastPressure) { lastPressure = info.pressure(); } return lastPressure; } lastPressure = 0.0; return 0.0; } private: qreal lastPressure; }; class KisDynamicSensorXTilt : public KisDynamicSensor { public: KisDynamicSensorXTilt(); ~KisDynamicSensorXTilt() override { } qreal value(const KisPaintInformation& info) override { return 1.0 - fabs(info.xTilt()) / 60.0; } }; class KisDynamicSensorYTilt : public KisDynamicSensor { public: KisDynamicSensorYTilt(); ~KisDynamicSensorYTilt() override { } qreal value(const KisPaintInformation& info) override { return 1.0 - fabs(info.yTilt()) / 60.0; } }; class KisDynamicSensorTiltDirection : public KisDynamicSensor { public: KisDynamicSensorTiltDirection(); ~KisDynamicSensorTiltDirection() override {} qreal value(const KisPaintInformation& info) override { return KisPaintInformation::tiltDirection(info, true); } }; class KisDynamicSensorTiltElevation : public KisDynamicSensor { public: KisDynamicSensorTiltElevation(); ~KisDynamicSensorTiltElevation() override {} qreal value(const KisPaintInformation& info) override { return KisPaintInformation::tiltElevation(info, 60.0, 60.0, true); } }; class KisDynamicSensorPerspective : public KisDynamicSensor { public: KisDynamicSensorPerspective(); ~KisDynamicSensorPerspective() override { } qreal value(const KisPaintInformation& info) override { return info.perspective(); } }; class KisDynamicSensorTangentialPressure : public KisDynamicSensor { public: KisDynamicSensorTangentialPressure(); ~KisDynamicSensorTangentialPressure() override { } qreal value(const KisPaintInformation& info) override { return info.tangentialPressure(); } }; #endif diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp index 80b7d34ef1..9bea3bdc80 100644 --- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp +++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.cpp @@ -1,304 +1,307 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ToolReferenceImages.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "ToolReferenceImagesWidget.h" #include "KisReferenceImageCollection.h" ToolReferenceImages::ToolReferenceImages(KoCanvasBase * canvas) : DefaultTool(canvas, false) { setObjectName("ToolReferenceImages"); } ToolReferenceImages::~ToolReferenceImages() { } void ToolReferenceImages::activate(ToolActivation toolActivation, const QSet &shapes) { DefaultTool::activate(toolActivation, shapes); auto kisCanvas = dynamic_cast(canvas()); connect(kisCanvas->image(), SIGNAL(sigNodeAddedAsync(KisNodeSP)), this, SLOT(slotNodeAdded(KisNodeSP))); connect(kisCanvas->imageView()->document(), &KisDocument::sigReferenceImagesLayerChanged, this, &ToolReferenceImages::slotNodeAdded); auto referenceImageLayer = document()->referenceImagesLayer(); if (referenceImageLayer) { setReferenceImageLayer(referenceImageLayer); } } void ToolReferenceImages::deactivate() { DefaultTool::deactivate(); } void ToolReferenceImages::slotNodeAdded(KisNodeSP node) { auto *referenceImagesLayer = dynamic_cast(node.data()); if (referenceImagesLayer) { setReferenceImageLayer(referenceImagesLayer); } } void ToolReferenceImages::setReferenceImageLayer(KisSharedPtr layer) { m_layer = layer; connect(layer.data(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); } void ToolReferenceImages::addReferenceImage() { auto kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) - KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImage"); + KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImage"); dialog.setCaption(i18n("Select a Reference Image")); QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { dialog.setDefaultDir(locations.first()); } QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; auto *reference = KisReferenceImage::fromFile(filename, *kisCanvas->coordinatesConverter(), canvas()->canvasWidget()); if (reference) { KisDocument *doc = document(); doc->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, {reference})); } } void ToolReferenceImages::pasteReferenceImage() { KisCanvas2* kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) - KisReferenceImage* reference = KisReferenceImage::fromClipboard(*kisCanvas->coordinatesConverter()); + KisReferenceImage* reference = KisReferenceImage::fromClipboard(*kisCanvas->coordinatesConverter()); if(reference) { KisDocument *doc = document(); doc->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, {reference})); } } void ToolReferenceImages::removeAllReferenceImages() { auto layer = m_layer.toStrongRef(); if (!layer) return; canvas()->addCommand(layer->removeReferenceImages(document(), layer->shapes())); } void ToolReferenceImages::loadReferenceImages() { auto kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) - KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImageCollection"); + KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::OpenFile, "OpenReferenceImageCollection"); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images"); dialog.setCaption(i18n("Load Reference Images")); QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { dialog.setDefaultDir(locations.first()); } QString filename = dialog.filename(); if (filename.isEmpty()) return; if (!QFileInfo(filename).exists()) return; QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1'.", filename)); return; } KisReferenceImageCollection collection; if (collection.load(&file)) { QList shapes; Q_FOREACH(auto *reference, collection.referenceImages()) { shapes.append(reference); } KisDocument *doc = document(); doc->addCommand(KisReferenceImagesLayer::addReferenceImages(doc, shapes)); } else { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not load reference images from '%1'.", filename)); } file.close(); } void ToolReferenceImages::saveReferenceImages() { auto layer = m_layer.toStrongRef(); if (!layer || layer->shapeCount() == 0) return; auto kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas) - KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "SaveReferenceImageCollection"); + KoFileDialog dialog(kisCanvas->viewManager()->mainWindow(), KoFileDialog::SaveFile, "SaveReferenceImageCollection"); dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images"); dialog.setCaption(i18n("Save Reference Images")); QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!locations.isEmpty()) { dialog.setDefaultDir(locations.first()); } QString filename = dialog.filename(); if (filename.isEmpty()) return; QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Could not open '%1' for saving.", filename)); return; } KisReferenceImageCollection collection(layer->referenceImages()); bool ok = collection.save(&file); file.close(); if (!ok) { QMessageBox::critical(nullptr, i18nc("@title:window", "Krita"), i18n("Failed to save reference images.")); } } void ToolReferenceImages::slotSelectionChanged() { auto layer = m_layer.toStrongRef(); if (!layer) return; m_optionsWidget->selectionChanged(layer->shapeManager()->selection()); updateActions(); } QList> ToolReferenceImages::createOptionWidgets() { // Instead of inheriting DefaultTool's multi-tab implementation, inherit straight from KoToolBase return KoToolBase::createOptionWidgets(); } QWidget *ToolReferenceImages::createOptionWidget() { if (!m_optionsWidget) { m_optionsWidget = new ToolReferenceImagesWidget(this); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(m_optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); m_optionsWidget->layout()->addWidget(specialSpacer); } return m_optionsWidget; - } +} bool ToolReferenceImages::isValidForCurrentLayer() const { return true; } KoShapeManager *ToolReferenceImages::shapeManager() const { auto layer = m_layer.toStrongRef(); return layer ? layer->shapeManager() : nullptr; } KoSelection *ToolReferenceImages::koSelection() const { auto manager = shapeManager(); return manager ? manager->selection() : nullptr; } void ToolReferenceImages::updateDistinctiveActions(const QList &) { action("object_group")->setEnabled(false); action("object_unite")->setEnabled(false); action("object_intersect")->setEnabled(false); action("object_subtract")->setEnabled(false); action("object_split")->setEnabled(false); action("object_ungroup")->setEnabled(false); } void ToolReferenceImages::deleteSelection() { auto layer = m_layer.toStrongRef(); if (!layer) return; QList shapes = koSelection()->selectedShapes(); if (!shapes.empty()) { canvas()->addCommand(layer->removeReferenceImages(document(), shapes)); } } KisDocument *ToolReferenceImages::document() const { auto kisCanvas = dynamic_cast(canvas()); return kisCanvas->imageView()->document(); } QList ToolReferenceImagesFactory::createActionsImpl() { - KisActionRegistry *actionRegistry = KisActionRegistry::instance(); - QList actions = DefaultToolFactory::createActionsImpl(); - - actions << actionRegistry->makeQAction("object_order_front"); - actions << actionRegistry->makeQAction("object_order_raise"); - actions << actionRegistry->makeQAction("object_order_lower"); - actions << actionRegistry->makeQAction("object_order_back"); - actions << actionRegistry->makeQAction("object_group"); - actions << actionRegistry->makeQAction("object_ungroup"); - actions << actionRegistry->makeQAction("object_transform_rotate_90_cw"); - actions << actionRegistry->makeQAction("object_transform_rotate_90_ccw"); - actions << actionRegistry->makeQAction("object_transform_rotate_180"); - actions << actionRegistry->makeQAction("object_transform_mirror_horizontally"); - actions << actionRegistry->makeQAction("object_transform_mirror_vertically"); - actions << actionRegistry->makeQAction("object_transform_reset"); - actions << actionRegistry->makeQAction("object_unite"); - actions << actionRegistry->makeQAction("object_intersect"); - actions << actionRegistry->makeQAction("object_subtract"); - actions << actionRegistry->makeQAction("object_split"); + QList defaultActions = DefaultToolFactory::createActionsImpl(); + QList actions; + + QStringList actionNames; + actionNames << "object_order_front" + << "object_order_raise" + << "object_order_lower" + << "object_order_back" + << "object_transform_rotate_90_cw" + << "object_transform_rotate_90_ccw" + << "object_transform_rotate_180" + << "object_transform_mirror_horizontally" + << "object_transform_mirror_vertically" + << "object_transform_reset"; + + Q_FOREACH(QAction *action, defaultActions) { + if (actionNames.contains(action->objectName())) { + actions << action; + } + } return actions; } diff --git a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h index 02d4a3c327..9a430a6c56 100644 --- a/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h +++ b/plugins/tools/defaulttool/referenceimagestool/ToolReferenceImages.h @@ -1,111 +1,112 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. + * the Free Software Foundation; version 2 of the License, or + * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TOOL_REFERENCE_IMAGES_H #define TOOL_REFERENCE_IMAGES_H #include #include #include #include #include "kis_painting_assistant.h" #include #include #include #include class ToolReferenceImagesWidget; class KisReferenceImagesLayer; class ToolReferenceImages : public DefaultTool { Q_OBJECT public: ToolReferenceImages(KoCanvasBase * canvas); ~ToolReferenceImages() override; virtual quint32 priority() { return 3; } void mouseDoubleClickEvent(KoPointerEvent */*event*/) override {} void deleteSelection() override; protected: QList> createOptionWidgets() override; QWidget *createOptionWidget() override; bool isValidForCurrentLayer() const override; KoShapeManager *shapeManager() const override; KoSelection *koSelection() const override; void updateDistinctiveActions(const QList &editableShapes) override; public Q_SLOTS: void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; void addReferenceImage(); void pasteReferenceImage(); void removeAllReferenceImages(); void saveReferenceImages(); void loadReferenceImages(); void slotNodeAdded(KisNodeSP node); void slotSelectionChanged(); private: friend class ToolReferenceImagesWidget; ToolReferenceImagesWidget *m_optionsWidget = nullptr; KisWeakSharedPtr m_layer; KisDocument *document() const; void setReferenceImageLayer(KisSharedPtr layer); }; class ToolReferenceImagesFactory : public DefaultToolFactory { public: ToolReferenceImagesFactory() : DefaultToolFactory("ToolReferenceImages") { setToolTip(i18n("Reference Images Tool")); setSection(TOOL_TYPE_VIEW); setIconName(koIconNameCStr("krita_tool_reference_images")); setPriority(2); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); }; ~ToolReferenceImagesFactory() override {} KoToolBase * createTool(KoCanvasBase * canvas) override { return new ToolReferenceImages(canvas); } QList createActionsImpl() override; }; #endif diff --git a/plugins/tools/selectiontools/kis_tool_select_outline.cc b/plugins/tools/selectiontools/kis_tool_select_outline.cc index 7b48eb24fc..43495b988d 100644 --- a/plugins/tools/selectiontools/kis_tool_select_outline.cc +++ b/plugins/tools/selectiontools/kis_tool_select_outline.cc @@ -1,282 +1,283 @@ /* * kis_tool_select_freehand.h - part of Krayon^WKrita * * Copyright (c) 2000 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2007 Sven Langkamp * Copyright (c) 2015 Michael Abrahams * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_select_outline.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_painter.h" #include #include "canvas/kis_canvas2.h" #include "kis_pixel_selection.h" #include "kis_selection_tool_helper.h" #include "kis_algebra_2d.h" #define FEEDBACK_LINE_WIDTH 2 KisToolSelectOutline::KisToolSelectOutline(KoCanvasBase * canvas) : KisToolSelect(canvas, KisCursor::load("tool_outline_selection_cursor.png", 5, 5), i18n("Outline Selection")), m_continuedMode(false) { } KisToolSelectOutline::~KisToolSelectOutline() { } void KisToolSelectOutline::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Control) { m_continuedMode = true; } KisToolSelect::keyPressEvent(event); } void KisToolSelectOutline::keyReleaseEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Control || !(event->modifiers() & Qt::ControlModifier)) { m_continuedMode = false; if (mode() != PAINT_MODE && !m_points.isEmpty()) { finishSelectionAction(); } } KisToolSelect::keyReleaseEvent(event); } void KisToolSelectOutline::mouseMoveEvent(KoPointerEvent *event) { KisToolSelect::mouseMoveEvent(event); if (selectionDragInProgress()) return; m_lastCursorPos = convertToPixelCoord(event); if (m_continuedMode && mode() != PAINT_MODE) { updateContinuedMode(); } } void KisToolSelectOutline::beginPrimaryAction(KoPointerEvent *event) { KisToolSelectBase::beginPrimaryAction(event); if (selectionDragInProgress()) return; if (!selectionEditable()) { event->ignore(); return; } setMode(KisTool::PAINT_MODE); if (m_continuedMode && !m_points.isEmpty()) { m_paintPath.lineTo(pixelToView(convertToPixelCoord(event))); } else { m_paintPath.moveTo(pixelToView(convertToPixelCoord(event))); } m_points.append(convertToPixelCoord(event)); } void KisToolSelectOutline::continuePrimaryAction(KoPointerEvent *event) { KisToolSelectBase::continuePrimaryAction(event); if (selectionDragInProgress()) return; CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); QPointF point = convertToPixelCoord(event); m_paintPath.lineTo(pixelToView(point)); m_points.append(point); updateFeedback(); } void KisToolSelectOutline::endPrimaryAction(KoPointerEvent *event) { const bool hadMoveInProgress = selectionDragInProgress(); KisToolSelectBase::endPrimaryAction(event); if (hadMoveInProgress) return; CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); if (!m_continuedMode) { finishSelectionAction(); + m_points.clear(); // ensure points are always cleared } } void KisToolSelectOutline::finishSelectionAction() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); kisCanvas->updateCanvas(); const QRectF boundingRect = KisAlgebra2D::accumulateBounds(m_points); const QRectF boundingViewRect = pixelToView(boundingRect); KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select by Outline")); if (helper.tryDeselectCurrentSelection(boundingViewRect, selectionAction())) { return; } if (m_points.count() > 2) { QApplication::setOverrideCursor(KisCursor::waitCursor()); const SelectionMode mode = helper.tryOverrideSelectionMode(kisCanvas->viewManager()->selection(), selectionMode(), selectionAction()); if (mode == PIXEL_SELECTION) { KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); KisPainter painter(tmpSel); painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace())); painter.setAntiAliasPolygonFill(antiAliasSelection()); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setStrokeStyle(KisPainter::StrokeStyleNone); painter.paintPolygon(m_points); QPainterPath cache; cache.addPolygon(m_points); cache.closeSubpath(); tmpSel->setOutlineCache(cache); helper.selectPixelSelection(tmpSel, selectionAction()); } else { KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); QTransform resolutionMatrix; resolutionMatrix.scale(1 / currentImage()->xRes(), 1 / currentImage()->yRes()); path->moveTo(resolutionMatrix.map(m_points[0])); for (int i = 1; i < m_points.count(); i++) path->lineTo(resolutionMatrix.map(m_points[i])); path->close(); path->normalize(); helper.addSelectionShape(path, selectionAction()); } QApplication::restoreOverrideCursor(); } m_points.clear(); m_paintPath = QPainterPath(); } void KisToolSelectOutline::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if ((mode() == KisTool::PAINT_MODE || m_continuedMode) && !m_points.isEmpty()) { QPainterPath outline = m_paintPath; if (m_continuedMode && mode() != KisTool::PAINT_MODE) { outline.lineTo(pixelToView(m_lastCursorPos)); } paintToolOutline(&gc, outline); } } void KisToolSelectOutline::updateFeedback() { if (m_points.count() > 1) { qint32 lastPointIndex = m_points.count() - 1; QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_points[lastPointIndex]).normalized(); updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH); updateCanvasPixelRect(updateRect); } } void KisToolSelectOutline::updateContinuedMode() { if (!m_points.isEmpty()) { qint32 lastPointIndex = m_points.count() - 1; QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_lastCursorPos).normalized(); updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH); updateCanvasPixelRect(updateRect); } } void KisToolSelectOutline::deactivate() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); kisCanvas->updateCanvas(); m_continuedMode = false; KisTool::deactivate(); } void KisToolSelectOutline::resetCursorStyle() { if (selectionAction() == SELECTION_ADD) { useCursor(KisCursor::load("tool_outline_selection_cursor_add.png", 6, 6)); } else if (selectionAction() == SELECTION_SUBTRACT) { useCursor(KisCursor::load("tool_outline_selection_cursor_sub.png", 6, 6)); } else { KisToolSelect::resetCursorStyle(); } }