├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── Makefile.vc ├── README.md ├── appveyor.yml ├── galib247.patch └── pngwolf.cxx /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "galib"] 2 | path = galib 3 | url = https://github.com/jibsen/galib.git 4 | branch = pngwolf-support 5 | [submodule "zlib"] 6 | path = zlib 7 | url = https://github.com/madler/zlib.git 8 | [submodule "zopfli"] 9 | path = zopfli 10 | url = https://github.com/google/zopfli.git 11 | [submodule "libdeflate"] 12 | path = libdeflate 13 | url = https://github.com/ebiggers/libdeflate.git 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - os: linux 7 | env: C_COMPILER=gcc-6 CXX_COMPILER=g++-6 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - gcc-6 14 | - g++-6 15 | 16 | - os: linux 17 | env: C_COMPILER=clang-3.8 CXX_COMPILER=clang++-3.8 18 | addons: 19 | apt: 20 | sources: 21 | - llvm-toolchain-precise-3.8 22 | - ubuntu-toolchain-r-test 23 | packages: 24 | - clang-3.8 25 | 26 | install: 27 | - git submodule -q update --init --recursive 28 | 29 | before_script: 30 | - if [ -n "${C_COMPILER}" ]; then export CC="${C_COMPILER}"; fi 31 | - if [ -n "${CXX_COMPILER}" ]; then export CXX="${CXX_COMPILER}"; fi 32 | - ${CXX} --version 33 | - cmake --version 34 | - mkdir build 35 | - cd build 36 | 37 | script: 38 | - cmake .. 39 | - make VERBOSE=1 40 | 41 | notifications: 42 | email: false 43 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.6) 2 | 3 | project(pngwolf) 4 | 5 | find_package(OpenMP) 6 | if(OPENMP_FOUND) 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 9 | endif() 10 | 11 | add_definitions(-DZLIB_CONST) 12 | 13 | if(MSVC) 14 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) 15 | elseif(CMAKE_COMPILER_IS_GNUCXX) 16 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 17 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 19 | endif() 20 | 21 | include_directories(galib libdeflate libdeflate/common zlib zopfli/src/zopfli) 22 | 23 | add_executable(pngwolf 24 | pngwolf.cxx 25 | galib/ga/GA1DArrayGenome.C 26 | galib/ga/GAAllele.C 27 | galib/ga/GABaseGA.C 28 | galib/ga/gabincvt.C 29 | galib/ga/GAGenome.C 30 | galib/ga/GAIncGA.C 31 | galib/ga/GAParameter.C 32 | galib/ga/GAPopulation.C 33 | galib/ga/garandom.C 34 | galib/ga/gaerror.C 35 | galib/ga/GAScaling.C 36 | galib/ga/GASelector.C 37 | galib/ga/GAStatistics.C 38 | libdeflate/lib/adler32.c 39 | libdeflate/lib/aligned_malloc.c 40 | libdeflate/lib/deflate_compress.c 41 | libdeflate/lib/x86_cpu_features.c 42 | libdeflate/lib/zlib_compress.c 43 | zlib/adler32.c 44 | zlib/compress.c 45 | zlib/crc32.c 46 | zlib/deflate.c 47 | zlib/infback.c 48 | zlib/inffast.c 49 | zlib/inflate.c 50 | zlib/inftrees.c 51 | zlib/trees.c 52 | zlib/uncompr.c 53 | zlib/zutil.c 54 | zopfli/src/zopfli/blocksplitter.c 55 | zopfli/src/zopfli/cache.c 56 | zopfli/src/zopfli/deflate.c 57 | zopfli/src/zopfli/gzip_container.c 58 | zopfli/src/zopfli/hash.c 59 | zopfli/src/zopfli/katajainen.c 60 | zopfli/src/zopfli/lz77.c 61 | zopfli/src/zopfli/squeeze.c 62 | zopfli/src/zopfli/tree.c 63 | zopfli/src/zopfli/util.c 64 | zopfli/src/zopfli/zlib_container.c 65 | zopfli/src/zopfli/zopfli_lib.c 66 | ) 67 | 68 | if(WIN32) 69 | target_link_libraries(pngwolf ws2_32) 70 | endif() 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## 2 | ## pngwolf-zopfli 3 | ## 4 | ## GCC Makefile 5 | ## 6 | 7 | .SUFFIXES: 8 | 9 | .PHONY: clean all 10 | 11 | CFLAGS = -std=c99 -Wall -msse2 -mfpmath=sse -O3 -flto -fopenmp 12 | CXXFLAGS = -std=c++14 -Wall -msse2 -mfpmath=sse -O3 -flto -fopenmp 13 | CPPFLAGS = -DNDEBUG -DZLIB_CONST -Igalib -Ilibdeflate -Izlib -Izopfli/src/zopfli 14 | 15 | ifeq ($(OS),Windows_NT) 16 | LDFLAGS += -static -s 17 | LDLIBS += -lws2_32 18 | ifeq ($(CC),cc) 19 | CC = gcc 20 | endif 21 | endif 22 | 23 | objs_pngwolf = pngwolf.o 24 | 25 | objs_galib = galib/ga/GA1DArrayGenome.o galib/ga/GAAllele.o \ 26 | galib/ga/GABaseGA.o galib/ga/gabincvt.o galib/ga/GAGenome.o \ 27 | galib/ga/GAIncGA.o galib/ga/GAParameter.o galib/ga/GAPopulation.o \ 28 | galib/ga/garandom.o galib/ga/gaerror.o galib/ga/GAScaling.o \ 29 | galib/ga/GASelector.o galib/ga/GAStatistics.o 30 | 31 | objs_libdeflate = libdeflate/lib/adler32.o libdeflate/lib/aligned_malloc.o \ 32 | libdeflate/lib/deflate_compress.o libdeflate/lib/x86_cpu_features.o \ 33 | libdeflate/lib/zlib_compress.o 34 | 35 | objs_zlib = zlib/adler32.o zlib/crc32.o zlib/deflate.o zlib/compress.o \ 36 | zlib/infback.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o \ 37 | zlib/uncompr.o zlib/zutil.o 38 | 39 | objs_zopfli = zopfli/src/zopfli/blocksplitter.o zopfli/src/zopfli/cache.o \ 40 | zopfli/src/zopfli/deflate.o zopfli/src/zopfli/gzip_container.o \ 41 | zopfli/src/zopfli/hash.o zopfli/src/zopfli/katajainen.o \ 42 | zopfli/src/zopfli/lz77.o zopfli/src/zopfli/squeeze.o \ 43 | zopfli/src/zopfli/tree.o zopfli/src/zopfli/util.o \ 44 | zopfli/src/zopfli/zlib_container.o zopfli/src/zopfli/zopfli_lib.o 45 | 46 | objs = $(objs_pngwolf) $(objs_galib) $(objs_libdeflate) $(objs_zlib) $(objs_zopfli) 47 | 48 | target = pngwolf 49 | 50 | all: $(target) 51 | 52 | %.o : %.cxx 53 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< 54 | 55 | galib/ga/%.o : galib/ga/%.C 56 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< 57 | 58 | libdeflate/lib/%.o : libdeflate/lib/%.c 59 | $(CC) $(CFLAGS) $(CPPFLAGS) -Ilibdeflate/common -c -o $@ $< 60 | 61 | zlib/%.o : zlib/%.c 62 | $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 63 | 64 | zopfli/src/zopfli/%.o : zopfli/src/zopfli/%.c 65 | $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< 66 | 67 | $(target): $(objs) 68 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ 69 | 70 | clean: 71 | $(RM) $(objs) $(target) 72 | -------------------------------------------------------------------------------- /Makefile.vc: -------------------------------------------------------------------------------- 1 | ## 2 | ## pngwolf-zopfli 3 | ## 4 | ## Visual C++ Makefile 5 | ## 6 | 7 | .SUFFIXES: 8 | .SUFFIXES: .obj .cxx .C .c 9 | 10 | CFLAGS = /W2 /O2 /fp:fast /GL /openmp 11 | CXXFLAGS = $(CFLAGS) /TP /EHsc 12 | CPPFLAGS = /DNDEBUG /DZLIB_CONST /Igalib /Ilibdeflate /Izlib /Izopfli\src\zopfli 13 | LDFLAGS = /release 14 | LDLIBS = ws2_32.lib 15 | 16 | !IF "$(PLATFORM)"=="X64" || "$(PLATFORM)"=="x64" 17 | LDFLAGS = $(LDFLAGS) /opt:ref,icf /subsystem:console,5.02 18 | !ELSEIF "$(PLATFORM)"=="X86" || "$(PLATFORM)"=="x86" 19 | LDFLAGS = $(LDFLAGS) /opt:ref /subsystem:console,5.01 20 | !ENDIF 21 | 22 | objs_pngwolf = pngwolf.obj 23 | 24 | objs_galib = galib\ga\GA1DArrayGenome.obj galib\ga\GAAllele.obj \ 25 | galib\ga\GABaseGA.obj galib\ga\gabincvt.obj galib\ga\GAGenome.obj \ 26 | galib\ga\GAIncGA.obj galib\ga\GAParameter.obj galib\ga\GAPopulation.obj \ 27 | galib\ga\garandom.obj galib\ga\gaerror.obj galib\ga\GAScaling.obj \ 28 | galib\ga\GASelector.obj galib\ga\GAStatistics.obj 29 | 30 | objs_libdeflate = libdeflate\lib\adler32.obj libdeflate\lib\aligned_malloc.obj \ 31 | libdeflate\lib\deflate_compress.obj libdeflate\lib\x86_cpu_features.obj \ 32 | libdeflate\lib\zlib_compress.obj 33 | 34 | objs_zlib = zlib\adler32.obj zlib\crc32.obj zlib\deflate.obj zlib\compress.obj \ 35 | zlib\infback.obj zlib\inffast.obj zlib\inflate.obj zlib\inftrees.obj zlib\trees.obj \ 36 | zlib\uncompr.obj zlib\zutil.obj 37 | 38 | objs_zopfli = zopfli\src\zopfli\blocksplitter.obj zopfli\src\zopfli\cache.obj \ 39 | zopfli\src\zopfli\deflate.obj zopfli\src\zopfli\gzip_container.obj \ 40 | zopfli\src\zopfli\hash.obj zopfli\src\zopfli\katajainen.obj \ 41 | zopfli\src\zopfli\lz77.obj zopfli\src\zopfli\squeeze.obj \ 42 | zopfli\src\zopfli\tree.obj zopfli\src\zopfli\util.obj \ 43 | zopfli\src\zopfli\zlib_container.obj zopfli\src\zopfli\zopfli_lib.obj 44 | 45 | objs = $(objs_pngwolf) $(objs_galib) $(objs_libdeflate) $(objs_zlib) $(objs_zopfli) 46 | 47 | target = pngwolf.exe 48 | 49 | all: $(target) 50 | 51 | .cxx.obj: 52 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) /c $< 53 | 54 | {galib\ga}.C{galib\ga}.obj:: 55 | $(CXX) /MP $(CXXFLAGS) $(CPPFLAGS) /Fogalib\ga\ /c $< 56 | 57 | {libdeflate\lib}.c{libdeflate\lib}.obj:: 58 | $(CC) /MP $(CFLAGS) $(CPPFLAGS) /Ilibdeflate\common /Folibdeflate\lib\ /c $< 59 | 60 | {zlib}.c{zlib}.obj:: 61 | $(CC) /MP $(CFLAGS) $(CPPFLAGS) /Fozlib\ /c $< 62 | 63 | {zopfli\src\zopfli}.c{zopfli\src\zopfli}.obj:: 64 | $(CC) /MP $(CFLAGS) $(CPPFLAGS) /Fozopfli\src\zopfli\ /c $< 65 | 66 | $(target): $(objs) 67 | $(CC) $(CFLAGS) $(CPPFLAGS) /Fe$@ $** /link $(LDFLAGS) $(LDLIBS) 68 | 69 | clean: 70 | del /Q $(objs) $(target) 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | pngwolf-zopfli 3 | ============== 4 | 5 | `pngwolf-zopfli` is a version of `pngwolf` that uses Zopfli for the 6 | final compression step. 7 | 8 | `pngwolf` is a tool to minimize the size of PNG image files. There are 9 | a number of factors that affect the size of PNG image files, such as 10 | the number of colors in the image and whether the image data is stored 11 | as RGBA data or in the form of references to a color palette. The main 12 | factor is the quality of the Deflate compression used to compress the 13 | image data, which is in turn affected by the quality of the compressor 14 | and how well the data to be compressed is arranged. 15 | 16 | The PNG format supports a number of scanline filters that transform the 17 | image data by relating nearby pixels mathematically. Choosing the right 18 | filters for each scanline can make the image data more compressible. It 19 | is, however, infeasible for non-trivial images to find the best filters 20 | so typical encoders rely on a couple of heuristics to find good filters. 21 | 22 | `pngwolf` employs a genetic algorithm to find better filter combinations 23 | than traditional heuristics. It derives a couple of filter combinations 24 | heuristically, adds a couple of random combinations, and then looks how 25 | well each combination compresses. Two very different combinations may 26 | compress similarily well, for instance, one combination may be very good 27 | for the first couple of scanlines, while the other may be very good for 28 | the last couple of scanlines. So taking the beginning of one combination 29 | and the tail of the other to make a new one may result in a combination 30 | that compresses better then the original two. 31 | 32 | That is, in essence, what `pngwolf` does, over and over again. Further, 33 | the most widely used PNG encoders use the zlib library for compression. 34 | The zlib library favours speed over compression ratio in some cases, so 35 | whatever filters are selected to aid compression, the result with zlib 36 | may not be the smallest possible. The Zopfli library has a Deflate 37 | encoder that favours size over speed at certain settings. So, `pngwolf` 38 | attempts to make use of both: a fast zlib setting is used to estimate 39 | how well some filter combination aids compression, and when it gets 40 | bored, it uses Zopfli to generate the final result. 41 | 42 | Doing this `pngwolf` is able to compress some images better than other 43 | optimizers (like `OptiPNG`, `AdvanceCOMP`, `pngcrush`, and `pngout`), 44 | either because it finds better filter combinations then they do, or 45 | because it uses Zopfli's Deflate implementation. It does not attempt to 46 | make other optimizations, like converting indexed images to RGB format. 47 | 48 | None of the tools mentioned, including `pngwolf` follow any kind of 49 | holistic approach to PNG optimization, so to get the best results they 50 | need to be used in combination (and sometimes applying them repeatedly 51 | or in different orders provides the best results). As far as I can tell 52 | most other tools do not try to preserve the filter combination in the 53 | original image, so `pngwolf` should usually be used last or 54 | second-to-last in the optimization process. 55 | 56 | For images that are already optimized using all the other tools, there 57 | is about `1%` further reduction to be expected from `pngwolf` for 58 | suitable images. Still, it should be rare to find images on the Web 59 | that `pngwolf` cannot compress a little bit further. 60 | 61 | The tool suffers from the lack of a Deflate encoder that makes it easy 62 | store the results of data analysis (where are duplicate substrings in 63 | the data) and combine them (if you recall the earlier example where it 64 | takes the head of one combination and the tail of another, an encoder 65 | would not have to analyze all of the two parts again, only where they 66 | overlap). So it can often take a long time (as in minutes) to find the 67 | best results. 68 | 69 | Regardless of the performance deficiency `pngwolf` is well-suited as a 70 | research tool to come up with better heuristics for filter selection, 71 | or to extend the genetic algorithm approach to other aspects of PNG 72 | optimization (the main thing being considered is re-arranging the 73 | entries in color palettes so the image data compresses better). The 74 | tool logs extensive information in a YAML-based machine-readable format 75 | while it attempts to optimize images which should aid in that. 76 | 77 | It also addresses two (other) user-interface issues I had in using the 78 | other tools, namely it allows you to make it stop trying to find better 79 | optimizations at well-specified points (such as the total time used), 80 | and if you start an optimization run but grow impatient and abort the 81 | program, results should not get lost, but should be stored anyway. 82 | 83 | 84 | How to Build 85 | ------------ 86 | 87 | To compile `pngwolf-zopfli` you need four additional libraries: 88 | 89 | * [GAlib](http://lancet.mit.edu/ga/dist/) 90 | * [libdeflate](https://github.com/ebiggers/libdeflate) 91 | * [zlib](http://zlib.net/) 92 | * [Zopfli](https://github.com/google/zopfli/) 93 | 94 | Put these into `galib`, `libdeflate`, `zlib`, and `zopfli` sub-directories 95 | into the directory where `pngwolf.cxx` is located. 96 | 97 | `galib247.patch` fixes an issue in GAlib 2.4.7 when compiling with GCC 98 | and Clang, and adds parallel evaluation using OpenMP. 99 | 100 | If you cloned the git repository, you can use 101 | `git submodule update --init --recursive` to retrieve these dependencies. 102 | 103 | Use the [CMake utility](http://www.cmake.org/) on the `CMakeLists.txt`, 104 | use one of the supplied makefiles, or simply specify all the files 105 | specified in `CMakeLists.txt` as input to your compiler. 106 | 107 | 108 | License 109 | ------- 110 | 111 | The original `pngwolf` is licensed under GPLv2-or-later. The `pngwolf-zopfli` 112 | version as a whole is licensed under GPLv3, to allow distribution of binaries 113 | linked with Zopfli (which is licensed under the Apache License, Version 2.0). 114 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.1.1-{build} 2 | 3 | environment: 4 | matrix: 5 | - GENERATOR: Visual Studio 14 2015 6 | - GENERATOR: Visual Studio 14 2015 Win64 7 | - GENERATOR: Visual Studio 12 2013 8 | - GENERATOR: Visual Studio 12 2013 Win64 9 | 10 | configuration: Debug 11 | 12 | install: 13 | - git submodule -q update --init --recursive 14 | 15 | before_build: 16 | - cmake --version 17 | - mkdir build 18 | - cd build 19 | 20 | build_script: 21 | - cmake -G "%GENERATOR%" .. 22 | - cmake --build . --config %CONFIGURATION% 23 | -------------------------------------------------------------------------------- /galib247.patch: -------------------------------------------------------------------------------- 1 | diff --git a/galib/ga/GA1DArrayGenome.C b/galib/ga/GA1DArrayGenome.C 2 | index d7471e8..24cc149 100644 3 | --- a/galib/ga/GA1DArrayGenome.C 4 | +++ b/galib/ga/GA1DArrayGenome.C 5 | @@ -50,7 +50,7 @@ GAGenome(DEFAULT_1DARRAY_INITIALIZER, 6 | evaluator(f); 7 | userData(u); 8 | nx=minX=maxX=length; 9 | - crossover(DEFAULT_1DARRAY_CROSSOVER); 10 | + this->crossover(DEFAULT_1DARRAY_CROSSOVER); 11 | } 12 | 13 | 14 | @@ -222,10 +222,10 @@ GA1DArrayGenome(length, f, u){ 15 | aset = new GAAlleleSet[1]; 16 | aset[0] = s; 17 | 18 | - initializer(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_INITIALIZER); 19 | - mutator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_MUTATOR); 20 | - comparator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_COMPARATOR); 21 | - crossover(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_CROSSOVER); 22 | + this->initializer(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_INITIALIZER); 23 | + this->mutator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_MUTATOR); 24 | + this->comparator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_COMPARATOR); 25 | + this->crossover(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_CROSSOVER); 26 | } 27 | 28 | template 29 | @@ -238,10 +238,10 @@ GA1DArrayGenome(sa.size(), f, u) { 30 | for(int i=0; i::DEFAULT_1DARRAY_ALLELE_INITIALIZER); 34 | - mutator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_MUTATOR); 35 | - comparator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_COMPARATOR); 36 | - crossover(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_CROSSOVER); 37 | + this->initializer(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_INITIALIZER); 38 | + this->mutator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_MUTATOR); 39 | + this->comparator(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_COMPARATOR); 40 | + this->crossover(GA1DArrayAlleleGenome::DEFAULT_1DARRAY_ALLELE_CROSSOVER); 41 | } 42 | 43 | 44 | diff --git a/galib/ga/GA2DArrayGenome.C b/galib/ga/GA2DArrayGenome.C 45 | index 64cede5..a9cc01c 100644 46 | --- a/galib/ga/GA2DArrayGenome.C 47 | +++ b/galib/ga/GA2DArrayGenome.C 48 | @@ -39,7 +39,7 @@ GAGenome(DEFAULT_2DARRAY_INITIALIZER, 49 | { 50 | evaluator(f); 51 | userData(u); 52 | - crossover(DEFAULT_2DARRAY_CROSSOVER); 53 | + this->crossover(DEFAULT_2DARRAY_CROSSOVER); 54 | nx=minX=maxX=width; ny=minY=maxY=height; 55 | } 56 | 57 | @@ -269,10 +269,10 @@ GA2DArrayGenome(width,height,f,u){ 58 | aset = new GAAlleleSet[1]; 59 | aset[0] = s; 60 | 61 | - initializer(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_INITIALIZER); 62 | - mutator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_MUTATOR); 63 | - comparator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_COMPARATOR); 64 | - crossover(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_CROSSOVER); 65 | + this->initializer(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_INITIALIZER); 66 | + this->mutator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_MUTATOR); 67 | + this->comparator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_COMPARATOR); 68 | + this->crossover(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_CROSSOVER); 69 | } 70 | 71 | template 72 | @@ -286,10 +286,10 @@ GA2DArrayGenome(width,height, f, u) { 73 | for(int i=0; i::DEFAULT_2DARRAY_ALLELE_INITIALIZER); 77 | - mutator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_MUTATOR); 78 | - comparator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_COMPARATOR); 79 | - crossover(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_CROSSOVER); 80 | + this->initializer(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_INITIALIZER); 81 | + this->mutator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_MUTATOR); 82 | + this->comparator(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_COMPARATOR); 83 | + this->crossover(GA2DArrayAlleleGenome::DEFAULT_2DARRAY_ALLELE_CROSSOVER); 84 | } 85 | 86 | 87 | diff --git a/galib/ga/GA3DArrayGenome.C b/galib/ga/GA3DArrayGenome.C 88 | index 62219d2..4fd4f38 100644 89 | --- a/galib/ga/GA3DArrayGenome.C 90 | +++ b/galib/ga/GA3DArrayGenome.C 91 | @@ -39,7 +39,7 @@ GAGenome(DEFAULT_3DARRAY_INITIALIZER, 92 | { 93 | evaluator(f); 94 | userData(u); 95 | - crossover(DEFAULT_3DARRAY_CROSSOVER); 96 | + this->crossover(DEFAULT_3DARRAY_CROSSOVER); 97 | nx=minX=maxX=w; ny=minY=maxY=h; nz=minZ=maxZ=d; 98 | } 99 | 100 | @@ -322,10 +322,10 @@ GA3DArrayGenome(w,h,d,f,u) { 101 | aset = new GAAlleleSet[1]; 102 | aset[0] = s; 103 | 104 | - initializer(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_INITIALIZER); 105 | - mutator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_MUTATOR); 106 | - comparator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_COMPARATOR); 107 | - crossover(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_CROSSOVER); 108 | + this->initializer(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_INITIALIZER); 109 | + this->mutator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_MUTATOR); 110 | + this->comparator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_COMPARATOR); 111 | + this->crossover(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_CROSSOVER); 112 | } 113 | 114 | template 115 | @@ -339,10 +339,10 @@ GA3DArrayGenome(w,h,d, f, u) { 116 | for(int i=0; i::DEFAULT_3DARRAY_ALLELE_INITIALIZER); 120 | - mutator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_MUTATOR); 121 | - comparator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_COMPARATOR); 122 | - crossover(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_CROSSOVER); 123 | + this->initializer(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_INITIALIZER); 124 | + this->mutator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_MUTATOR); 125 | + this->comparator(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_COMPARATOR); 126 | + this->crossover(GA3DArrayAlleleGenome::DEFAULT_3DARRAY_ALLELE_CROSSOVER); 127 | } 128 | 129 | 130 | diff --git a/galib/ga/GAIncGA.C b/galib/ga/GAIncGA.C 131 | index 77b38ab..5f73e6c 100644 132 | --- a/galib/ga/GAIncGA.C 133 | +++ b/galib/ga/GAIncGA.C 134 | @@ -237,6 +237,16 @@ GAIncrementalGA::step() 135 | 136 | stats.numeval += c1 + c2; 137 | 138 | +#ifdef _OPENMP 139 | + #pragma omp parallel sections 140 | + { 141 | + #pragma omp section 142 | + child1->evaluate(); 143 | + #pragma omp section 144 | + child2->evaluate(); 145 | + } 146 | +#endif 147 | + 148 | if(rs == PARENT){ 149 | child1 = pop->replace(child1, mom); 150 | if(mom == dad) // this is a possibility, if so do worst 151 | diff --git a/galib/ga/gaerror.C b/galib/ga/gaerror.C 152 | index c19d03d..8600518 100644 153 | --- a/galib/ga/gaerror.C 154 | +++ b/galib/ga/gaerror.C 155 | @@ -21,7 +21,7 @@ char _gaerrbuf2[120]; 156 | static STD_OSTREAM *__gaErrStream = & STD_CERR; 157 | #endif 158 | static GABoolean __gaErrFlag = gaTrue; 159 | -static char *__gaErrStr[] = { 160 | +static const char *__gaErrStr[] = { 161 | "error reading from file: ", 162 | "error writing to file: ", 163 | "unexpected EOF encountered during read.", 164 | -------------------------------------------------------------------------------- /pngwolf.cxx: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////// 2 | // 3 | // pngwolf - Optimize PNG file size by genetically finding filters 4 | // 5 | // Copyright (C) 2008-2011 Bjoern Hoehrmann 6 | // 7 | // Modified to use Zopfli for the final compression step 8 | // Copyright (C) 2015-2017 Joergen Ibsen 9 | // 10 | // This program is free software; you can redistribute it and/or 11 | // modify it under the terms of the GNU General Public License 12 | // as published by the Free Software Foundation; either version 2 13 | // of the License, or (at your option) any later version. 14 | // 15 | // This program is distributed in the hope that it will be useful, 16 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | // GNU General Public License for more details. 19 | // 20 | // $Id$ 21 | // 22 | //////////////////////////////////////////////////////////////////// 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #if defined(_MSC_VER) || defined(__MINGW32__) 45 | #include 46 | #else 47 | #include 48 | #endif 49 | 50 | #include "ga/ga.h" 51 | #include "libdeflate.h" 52 | #include "zlib.h" 53 | #include "zopfli.h" 54 | 55 | #ifndef SIZE_MAX 56 | #define SIZE_MAX ((size_t)-1) 57 | #endif 58 | 59 | #ifdef _MSC_VER 60 | #pragma warning(push, 4) 61 | #pragma warning(disable: 4996) 62 | #pragma warning(disable: 4100) 63 | #endif 64 | 65 | #define PNGWOLF_VERSION "1.1.2" 66 | 67 | //////////////////////////////////////////////////////////////////// 68 | // Miscellaneous structures and types 69 | //////////////////////////////////////////////////////////////////// 70 | enum PngFilter { 71 | None = 0, 72 | Sub = 1, 73 | Up = 2, 74 | Avg = 3, 75 | Paeth = 4 76 | }; 77 | 78 | struct PngChunk { 79 | uint32_t size; 80 | uint32_t type; 81 | uint32_t crc32; 82 | std::vector data; 83 | }; 84 | 85 | struct IhdrChunk { 86 | uint32_t width; 87 | uint32_t height; 88 | uint32_t depth; 89 | uint32_t color; 90 | uint32_t comp; 91 | uint32_t filter; 92 | uint32_t interlace; 93 | }; 94 | 95 | using PngFilterGenome = GA1DArrayAlleleGenome; 96 | 97 | class Deflater { 98 | public: 99 | virtual std::vector deflate(const std::vector&) = 0; 100 | virtual bool parse_option(const std::string&) = 0; 101 | virtual ~Deflater() {} 102 | }; 103 | 104 | class PngWolf { 105 | public: 106 | // IHDR data 107 | IhdrChunk ihdr; 108 | 109 | // Derived IHDR data 110 | size_t scanline_width; 111 | size_t scanline_delta; 112 | 113 | // The input image as list of chunks 114 | std::list chunks; 115 | 116 | // Urfilter 117 | std::vector original_filters; 118 | 119 | // Filters 120 | std::map> genomes; 121 | 122 | std::vector> best_genomes; 123 | 124 | // ... 125 | GAPopulation initial_pop; 126 | 127 | // Command line options 128 | unsigned max_stagnate_time; 129 | unsigned max_time; 130 | unsigned max_evaluations; 131 | unsigned max_deflate; 132 | size_t population_size; 133 | const char* in_path = nullptr; 134 | const char* out_path = nullptr; 135 | bool verbose_analysis; 136 | bool verbose_summary; 137 | bool verbose_genomes; 138 | bool exclude_singles; 139 | bool exclude_original; 140 | bool exclude_heuristic; 141 | bool exclude_experiment1; 142 | bool exclude_experiment2; 143 | bool exclude_experiment3; 144 | bool exclude_experiment4; 145 | bool normalize_alpha; 146 | bool even_if_bigger; 147 | bool auto_mpass; 148 | bool bigger_is_better; 149 | int zlib_level; 150 | std::vector strip_chunks; 151 | bool strip_optional; 152 | std::vector keep_chunks; 153 | 154 | // 155 | std::unique_ptr deflate_estimator; 156 | std::unique_ptr deflate_out; 157 | 158 | // User input 159 | bool should_abort; 160 | 161 | // Keeping track of time 162 | time_t program_begun_at; 163 | time_t search_begun_at; 164 | time_t last_improvement_at; 165 | time_t last_step_at; 166 | time_t done_deflating_at; 167 | 168 | // IDAT 169 | std::vector original_inflated; 170 | std::vector original_deflated; 171 | std::vector original_unfiltered; 172 | 173 | // 174 | std::map> flt_singles; 175 | 176 | // 177 | std::map invis_colors; 178 | 179 | // 180 | unsigned nth_generation; 181 | unsigned genomes_evaluated; 182 | 183 | // 184 | std::vector best_inflated; 185 | std::vector best_deflated; 186 | 187 | // The genetic algorithm 188 | GAGeneticAlgorithm* ga = nullptr; 189 | 190 | // Logging 191 | void log_analysis(); 192 | void log_critter(const PngFilterGenome& curr_best); 193 | void log_summary(); 194 | void log_genome(const PngFilterGenome& ge); 195 | 196 | // Various 197 | bool read_file(); 198 | bool save_file(); 199 | bool save_best_idat(const char* path); 200 | bool save_original_idat(const char* path); 201 | bool save_idat(const char* path, std::vector& deflated, std::vector& inflated); 202 | void init_filters(); 203 | void run(); 204 | void recompress(); 205 | std::vector refilter(const PngFilterGenome& ge); 206 | bool recompress_optional_chunk(PngChunk &chunk); 207 | 208 | // Constructor 209 | PngWolf() : 210 | should_abort(false), 211 | done_deflating_at(0), 212 | nth_generation(0), 213 | genomes_evaluated(0) { 214 | } 215 | 216 | ~PngWolf() { 217 | // TODO: This should probably delete the genomes, both 218 | // the ge_ ones and the ones in the best_genomes vector 219 | } 220 | }; 221 | 222 | struct DeflateLibdeflate : public Deflater { 223 | public: 224 | std::vector deflate(const std::vector& inflated) override { 225 | struct libdeflate_compressor *compressor = nullptr; 226 | 227 | compressor = libdeflate_alloc_compressor(z_level); 228 | 229 | if (compressor == nullptr) 230 | abort(); 231 | 232 | const size_t maxsize = libdeflate_zlib_compress_bound(compressor, inflated.size()); 233 | std::vector new_deflated(maxsize); 234 | 235 | size_t res = libdeflate_zlib_compress(compressor, inflated.data(), inflated.size(), 236 | new_deflated.data(), new_deflated.size()); 237 | 238 | libdeflate_free_compressor(compressor); 239 | 240 | // TODO: aborting here probably leaks memory 241 | if (res == 0) 242 | abort(); 243 | 244 | new_deflated.resize(res); 245 | 246 | return new_deflated; 247 | } 248 | 249 | bool parse_option(const std::string& opt) override { 250 | auto i = opt.find('='); 251 | 252 | if (i == std::string::npos) { 253 | return false; 254 | } 255 | 256 | if (opt.compare(0, i, "level") == 0) { 257 | int level = stoi(opt.substr(i + 1)); 258 | 259 | if (level >= 1 && level <= 12) { 260 | z_level = level; 261 | return true; 262 | } 263 | } 264 | 265 | return false; 266 | } 267 | 268 | DeflateLibdeflate(int level=4) : 269 | z_level(level) { 270 | } 271 | 272 | private: 273 | int z_level; 274 | }; 275 | 276 | struct DeflateZlib : public Deflater { 277 | public: 278 | std::vector deflate(const std::vector& inflated) override { 279 | z_stream strm; 280 | 281 | strm.zalloc = Z_NULL; 282 | strm.zfree = Z_NULL; 283 | strm.opaque = Z_NULL; 284 | 285 | if (deflateInit2(&strm, z_level, Z_DEFLATED, 286 | z_windowBits, z_memLevel, z_strategy) != Z_OK) { 287 | // TODO: 288 | abort(); 289 | } 290 | 291 | strm.next_in = const_cast(inflated.data()); 292 | strm.avail_in = inflated.size(); 293 | 294 | size_t max = deflateBound(&strm, inflated.size()); 295 | std::vector new_deflated(max); 296 | 297 | strm.next_out = new_deflated.data(); 298 | strm.avail_out = new_deflated.size(); 299 | 300 | // TODO: aborting here probably leaks memory 301 | if (::deflate(&strm, Z_FINISH) != Z_STREAM_END) 302 | abort(); 303 | 304 | new_deflated.resize(max - strm.avail_out); 305 | 306 | deflateEnd(&strm); 307 | 308 | return new_deflated; 309 | } 310 | 311 | bool parse_option(const std::string& opt) override { 312 | auto i = opt.find('='); 313 | 314 | if (i == std::string::npos) { 315 | return false; 316 | } 317 | 318 | if (opt.compare(0, i, "level") == 0) { 319 | int level = stoi(opt.substr(i + 1)); 320 | 321 | if (level >= 0 && level <= 9) { 322 | z_level = level; 323 | return true; 324 | } 325 | } else if (opt.compare(0, i, "memlevel") == 0) { 326 | int memlevel = stoi(opt.substr(i + 1)); 327 | 328 | if (memlevel >= 1 && memlevel <= 9) { 329 | z_memLevel = memlevel; 330 | return true; 331 | } 332 | } else if (opt.compare(0, i, "strategy") == 0) { 333 | int strategy = stoi(opt.substr(i + 1)); 334 | 335 | if (strategy == Z_DEFAULT_STRATEGY || strategy == Z_FILTERED 336 | || strategy == Z_HUFFMAN_ONLY || strategy == Z_RLE) { 337 | z_strategy = strategy; 338 | return true; 339 | } 340 | } else if (opt.compare(0, i, "window") == 0) { 341 | int windowbits = stoi(opt.substr(i + 1)); 342 | 343 | if (windowbits >= 8 && windowbits <= 15) { 344 | z_windowBits = windowbits; 345 | return true; 346 | } 347 | } 348 | 349 | return false; 350 | } 351 | 352 | DeflateZlib(int level=3, int windowBits=15, int memLevel=8, int strategy=0) : 353 | z_level(level), 354 | z_windowBits(windowBits), 355 | z_memLevel(memLevel), 356 | z_strategy(strategy) { 357 | } 358 | 359 | private: 360 | int z_level; 361 | int z_windowBits; 362 | int z_memLevel; 363 | int z_strategy; 364 | }; 365 | 366 | struct DeflateZopfli : public Deflater { 367 | public: 368 | std::vector deflate(const std::vector& inflated) override { 369 | 370 | ZopfliOptions zopt; 371 | unsigned char* pout = 0; 372 | size_t outsize = 0; 373 | 374 | ZopfliInitOptions(&zopt); 375 | 376 | zopt.verbose = zop_verbose > 0; 377 | zopt.verbose_more = zop_verbose > 1; 378 | zopt.numiterations = zop_iter; 379 | zopt.blocksplittingmax = zop_maxsplit; 380 | 381 | // TODO: figure out what to do with errors here 382 | 383 | ZopfliCompress(&zopt, ZOPFLI_FORMAT_ZLIB, 384 | inflated.data(), 385 | inflated.size(), &pout, &outsize); 386 | 387 | std::vector deflated(pout, pout + outsize); 388 | 389 | free(pout); 390 | 391 | return deflated; 392 | } 393 | 394 | bool parse_option(const std::string& opt) override { 395 | auto i = opt.find('='); 396 | 397 | if (i == std::string::npos) { 398 | return false; 399 | } 400 | 401 | if (opt.compare(0, i, "iter") == 0) { 402 | int iter = stoi(opt.substr(i + 1)); 403 | 404 | if (iter > 0) { 405 | zop_iter = iter; 406 | return true; 407 | } 408 | } else if (opt.compare(0, i, "maxsplit") == 0) { 409 | int maxsplit = stoi(opt.substr(i + 1)); 410 | 411 | if (maxsplit >= 0) { 412 | zop_maxsplit = maxsplit; 413 | return true; 414 | } 415 | } else if (opt.compare(0, i, "verbose") == 0) { 416 | int verbose = stoi(opt.substr(i + 1)); 417 | 418 | if (verbose >= 0 && verbose <= 2) { 419 | zop_verbose = verbose; 420 | return true; 421 | } 422 | } 423 | 424 | return false; 425 | } 426 | 427 | DeflateZopfli(int iter=15, int maxsplit=15, int verbose=0) : 428 | zop_iter(iter), 429 | zop_maxsplit(maxsplit), 430 | zop_verbose(verbose) { 431 | } 432 | 433 | private: 434 | int zop_iter; 435 | int zop_maxsplit; 436 | int zop_verbose; 437 | }; 438 | 439 | static const char PNG_MAGIC[] = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; 440 | static const uint32_t IHDR_TYPE = 0x49484452; 441 | static const uint32_t PLTE_TYPE = 0x504c5445; 442 | static const uint32_t tRNS_TYPE = 0x74524e53; 443 | static const uint32_t IDAT_TYPE = 0x49444154; 444 | static const uint32_t IEND_TYPE = 0x49454e44; 445 | static const uint32_t tEXt_TYPE = 0x74455874; 446 | static const uint32_t iTXt_TYPE = 0x69545874; 447 | static const uint32_t zTXt_TYPE = 0x7a545874; 448 | static const uint32_t iCCP_TYPE = 0x69434350; 449 | 450 | //////////////////////////////////////////////////////////////////// 451 | // Global PngWolf instance 452 | //////////////////////////////////////////////////////////////////// 453 | static PngWolf wolf; 454 | 455 | //////////////////////////////////////////////////////////////////// 456 | // PNG Scanline Filters 457 | //////////////////////////////////////////////////////////////////// 458 | 459 | unsigned char paeth_predictor(unsigned char a, unsigned char b, unsigned char c) { 460 | unsigned int p = a + b - c; 461 | unsigned int pa = abs((int)(p - a)); 462 | unsigned int pb = abs((int)(p - b)); 463 | unsigned int pc = abs((int)(p - c)); 464 | 465 | if (pa <= pb && pa <= pc) 466 | return a; 467 | 468 | if (pb <= pc) 469 | return b; 470 | 471 | return c; 472 | } 473 | 474 | void filter_row_none(unsigned char* src, unsigned char* dst, size_t row, size_t pwidth, size_t bytes) { 475 | size_t xix = row * bytes + 1; 476 | memcpy(dst + xix, src + xix, bytes - 1); 477 | } 478 | 479 | void filter_row_sub(unsigned char* src, unsigned char* dst, size_t row, size_t pwidth, size_t bytes) { 480 | size_t xix = row * bytes + 1; 481 | size_t aix = xix; 482 | size_t end = (row+1)*bytes; 483 | 484 | for (; xix < row * bytes + 1 + pwidth; ++xix) 485 | dst[xix] = src[xix]; 486 | 487 | for (; xix < end; ++xix, ++aix) 488 | dst[xix] = src[xix] - src[aix]; 489 | } 490 | 491 | void filter_row_up(unsigned char* src, unsigned char* dst, size_t row, size_t pwidth, size_t bytes) { 492 | size_t xix = row * bytes + 1; 493 | size_t bix = xix - bytes; 494 | size_t end = (row+1)*bytes; 495 | 496 | if (row == 0) { 497 | memcpy(dst + 1, src + 1, bytes - 1); 498 | return; 499 | } 500 | 501 | for (; xix < end; ++xix, ++bix) 502 | dst[xix] = src[xix] - src[bix]; 503 | } 504 | 505 | void filter_row_avg(unsigned char* src, unsigned char* dst, size_t row, size_t pwidth, size_t bytes) { 506 | size_t xix = row * bytes + 1; 507 | size_t bix = xix - bytes; 508 | size_t aix = xix; 509 | size_t end = (row+1)*bytes; 510 | 511 | if (row == 0) { 512 | for (; xix < row * bytes + 1 + pwidth; ++xix) 513 | dst[xix] = src[xix]; 514 | 515 | for (; xix < end; ++xix, ++aix) 516 | dst[xix] = src[xix] - (src[aix] >> 1); 517 | 518 | return; 519 | } 520 | 521 | for (; xix < row * bytes + 1 + pwidth; ++xix, ++bix) 522 | dst[xix] = src[xix] - (src[bix] >> 1); 523 | 524 | for (; xix < end; ++xix, ++aix, ++bix) 525 | dst[xix] = src[xix] - ((src[aix] + src[bix]) >> 1); 526 | } 527 | 528 | void filter_row_paeth(unsigned char* src, unsigned char* dst, size_t row, size_t pwidth, size_t bytes) { 529 | size_t xix = row * bytes + 1; 530 | size_t aix = xix; 531 | size_t bix = xix - bytes; 532 | size_t cix = xix - bytes; 533 | size_t end = (row+1)*bytes; 534 | 535 | if (row == 0) { 536 | for (; xix < row * bytes + 1 + pwidth; ++xix) 537 | dst[xix] = src[xix]; 538 | 539 | for (; xix < end; ++xix, ++aix) 540 | dst[xix] = src[xix] - paeth_predictor(src[aix], 0 , 0); 541 | 542 | return; 543 | } 544 | 545 | // TODO: this should not change pwidth 546 | for (; pwidth > 0; --pwidth, ++xix, ++bix) 547 | dst[xix] = src[xix] - paeth_predictor(0, src[bix] , 0); 548 | 549 | for (; xix < end; ++xix, ++aix, ++bix, ++cix) 550 | dst[xix] = src[xix] - paeth_predictor(src[aix], src[bix], src[cix]); 551 | } 552 | 553 | void unfilter_row_sub(unsigned char* idat, size_t row, size_t pwidth, size_t bytes) { 554 | size_t xix = row * bytes + 1; 555 | size_t aix = xix; 556 | size_t end = (row+1)*bytes; 557 | 558 | xix += pwidth; 559 | while (xix < end) 560 | idat[xix++] += idat[aix++]; 561 | } 562 | 563 | void unfilter_row_up(unsigned char* idat, size_t row, size_t pwidth, size_t bytes) { 564 | size_t xix = row * bytes + 1; 565 | size_t bix = xix - bytes; 566 | size_t end = (row+1)*bytes; 567 | 568 | if (row == 0) 569 | return; 570 | while (xix < end) 571 | idat[xix++] += idat[bix++]; 572 | } 573 | 574 | void unfilter_row_avg(unsigned char* idat, size_t row, size_t pwidth, size_t bytes) { 575 | size_t xix = row * bytes + 1; 576 | size_t bix = xix - bytes; 577 | size_t end = (row+1)*bytes; 578 | size_t aix; 579 | 580 | if (row == 0) { 581 | size_t aix = xix; 582 | xix += pwidth; 583 | while (xix < end) 584 | idat[xix++] += idat[aix++] >> 1; 585 | return; 586 | } 587 | 588 | aix = xix; 589 | for (; pwidth > 0; --pwidth) 590 | idat[xix++] += idat[bix++] >> 1; 591 | 592 | while (xix < end) 593 | idat[xix++] += (idat[aix++] + idat[bix++]) >> 1; 594 | } 595 | 596 | void unfilter_row_paeth(unsigned char* idat, size_t row, size_t pwidth, size_t bytes) { 597 | size_t xix = row * bytes + 1; 598 | size_t bix = xix - bytes; 599 | size_t aix, cix; 600 | size_t end = (row+1)*bytes; 601 | 602 | if (row == 0) { 603 | size_t aix = xix; 604 | xix += pwidth; 605 | while (xix < end) 606 | idat[xix++] += paeth_predictor(idat[aix++], 0 , 0); 607 | return; 608 | } 609 | 610 | aix = xix; 611 | cix = aix - bytes; 612 | 613 | for (; pwidth > 0; --pwidth) 614 | idat[xix++] += paeth_predictor(0, idat[bix++] , 0); 615 | 616 | while (xix < end) 617 | idat[xix++] += paeth_predictor(idat[aix++], idat[bix++] , idat[cix++]); 618 | } 619 | 620 | void unfilter_idat(unsigned char* idat, size_t rows, size_t pwidth, size_t bytes) { 621 | size_t row; 622 | for (row = 0; row < rows; ++row) { 623 | switch(idat[row*bytes]) { 624 | case 0: 625 | break; 626 | case 1: 627 | unfilter_row_sub(idat, row, pwidth, bytes); 628 | break; 629 | case 2: 630 | unfilter_row_up(idat, row, pwidth, bytes); 631 | break; 632 | case 3: 633 | unfilter_row_avg(idat, row, pwidth, bytes); 634 | break; 635 | case 4: 636 | unfilter_row_paeth(idat, row, pwidth, bytes); 637 | break; 638 | default: 639 | assert(!"bad filter type"); 640 | } 641 | idat[row*bytes] = 0; 642 | } 643 | } 644 | 645 | void filter_idat(unsigned char* src, unsigned char* dst, const PngFilterGenome& filter, size_t pwidth, size_t bytes) { 646 | for (int row = 0; row < filter.size(); ++row) { 647 | switch(filter.gene(row)) { 648 | case 0: 649 | filter_row_none(src, dst, row, pwidth, bytes); 650 | break; 651 | case 1: 652 | filter_row_sub(src, dst, row, pwidth, bytes); 653 | break; 654 | case 2: 655 | filter_row_up(src, dst, row, pwidth, bytes); 656 | break; 657 | case 3: 658 | filter_row_avg(src, dst, row, pwidth, bytes); 659 | break; 660 | case 4: 661 | filter_row_paeth(src, dst, row, pwidth, bytes); 662 | break; 663 | default: 664 | assert(!"bad filter type"); 665 | } 666 | // TODO: check that src uses the `none` filter 667 | dst[row*bytes] = (unsigned char)filter.gene(row); 668 | } 669 | } 670 | 671 | //////////////////////////////////////////////////////////////////// 672 | // Signal handlers 673 | //////////////////////////////////////////////////////////////////// 674 | #if defined(_MSC_VER) || defined(__MINGW32__) 675 | BOOL WINAPI console_event_handler(DWORD Event) { 676 | switch (Event) { 677 | case CTRL_C_EVENT: 678 | case CTRL_BREAK_EVENT: 679 | case CTRL_CLOSE_EVENT: 680 | case CTRL_LOGOFF_EVENT: 681 | case CTRL_SHUTDOWN_EVENT: 682 | wolf.should_abort = true; 683 | return TRUE; 684 | } 685 | return FALSE; 686 | } 687 | #else 688 | void sigint_handler(int signum) { 689 | wolf.should_abort = true; 690 | } 691 | #endif 692 | 693 | //////////////////////////////////////////////////////////////////// 694 | // Genome Helpers 695 | //////////////////////////////////////////////////////////////////// 696 | template <> PngFilter 697 | GAAlleleSet::allele() const { 698 | return (PngFilter)GARandomInt(lower(), upper()); 699 | } 700 | 701 | std::vector inflate_zlib(std::vector& deflated) { 702 | z_stream strm; 703 | strm.zalloc = Z_NULL; 704 | strm.zfree = Z_NULL; 705 | strm.opaque = Z_NULL; 706 | strm.next_in = deflated.data(); 707 | strm.avail_in = deflated.size(); 708 | std::vector inflated; 709 | std::vector temp(65535); 710 | 711 | if (inflateInit(&strm) != Z_OK) 712 | goto error; 713 | 714 | do { 715 | strm.next_out = temp.data(); 716 | strm.avail_out = temp.size(); 717 | int ret = inflate(&strm, Z_NO_FLUSH); 718 | 719 | // TODO: going to `error` here probably leaks some memory 720 | // but it would be freed when exiting the process, so this 721 | // is mostly important when turning this into a library. 722 | if (ret != Z_STREAM_END && ret != Z_OK) 723 | goto error; 724 | 725 | size_t have = temp.size() - strm.avail_out; 726 | inflated.insert(inflated.end(), 727 | temp.begin(), temp.begin() + have); 728 | 729 | } while (strm.avail_out == 0); 730 | 731 | if (inflateEnd(&strm) != Z_OK) 732 | goto error; 733 | 734 | return inflated; 735 | 736 | error: 737 | // TODO: ... 738 | abort(); 739 | return inflated; 740 | } 741 | 742 | std::vector PngWolf::refilter(const PngFilterGenome& ge) { 743 | std::vector refiltered(original_unfiltered.size()); 744 | 745 | filter_idat(original_unfiltered.data(), refiltered.data(), ge, 746 | scanline_delta, scanline_width); 747 | 748 | return refiltered; 749 | } 750 | 751 | float Evaluator(GAGenome& genome) { 752 | PngFilterGenome& ge = 753 | (PngFilterGenome&)genome; 754 | 755 | // TODO: wolf should be user data, not a global 756 | if (wolf.should_abort) 757 | return FLT_MAX; 758 | 759 | #ifdef _OPENMP 760 | #pragma omp atomic 761 | #endif 762 | wolf.genomes_evaluated++; 763 | 764 | if (wolf.flt_singles.begin() == wolf.flt_singles.end()) { 765 | // TODO: ... 766 | abort(); 767 | } 768 | 769 | // TODO: it would be better to do this incrementally. 770 | 771 | std::vector filtered(wolf.original_unfiltered.size()); 772 | 773 | for (int row = 0; row < ge.size(); ++row) { 774 | size_t pos = wolf.scanline_width * row; 775 | memcpy(&filtered[pos], 776 | &wolf.flt_singles[ge.gene(row)][pos], wolf.scanline_width); 777 | } 778 | 779 | std::vector deflated = wolf.deflate_estimator->deflate(filtered); 780 | 781 | return float(deflated.size()); 782 | } 783 | 784 | //////////////////////////////////////////////////////////////////// 785 | // Helper 786 | //////////////////////////////////////////////////////////////////// 787 | unsigned sum_abs(unsigned c1, unsigned char c2) { 788 | return c1 + (c2 < 128 ? c2 : 256 - c2); 789 | } 790 | 791 | void update_chunk_crc(PngChunk &chunk) { 792 | const uint32_t chunk_type_network = htonl(chunk.type); 793 | 794 | chunk.crc32 = crc32(0L, Z_NULL, 0); 795 | 796 | chunk.crc32 = crc32(chunk.crc32, 797 | (const Bytef*)&chunk_type_network, 798 | sizeof(chunk_type_network)); 799 | 800 | chunk.crc32 = crc32(chunk.crc32, 801 | (const Bytef*)&chunk.data[0], 802 | chunk.data.size()); 803 | } 804 | 805 | //////////////////////////////////////////////////////////////////// 806 | // Logging 807 | //////////////////////////////////////////////////////////////////// 808 | void PngWolf::log_genome(const PngFilterGenome& ge) { 809 | for (int gix = 0; gix < ge.size(); ++gix) { 810 | if (gix % 72 == 0) 811 | fprintf(stdout, "\n "); 812 | fprintf(stdout, "%1d", ge.gene(gix)); 813 | } 814 | fprintf(stdout, "\n"); 815 | } 816 | 817 | void PngWolf::log_summary() { 818 | 819 | int diff = original_deflated.size() - best_deflated.size(); 820 | 821 | if (verbose_summary) { 822 | fprintf(stdout, "best filter sequence found:"); 823 | log_genome(*best_genomes.back()); 824 | fprintf(stdout, "" 825 | "best deflated idat size: %0.0f\n" 826 | "total time spent optimizing: %0.0f\n" 827 | "number of genomes evaluated: %u\n" 828 | "size of Zopfli deflated data: %u\n" 829 | "size difference to original: %d\n", 830 | best_genomes.back()->score(), 831 | difftime(time(NULL), program_begun_at), 832 | genomes_evaluated, 833 | (unsigned int) best_deflated.size(), 834 | -diff); 835 | } 836 | 837 | if (diff >= 0) 838 | fprintf(stdout, "# IDAT %d bytes smaller\n", diff); 839 | else 840 | fprintf(stdout, "# IDAT %d bytes bigger\n", abs(diff)); 841 | 842 | fflush(stdout); 843 | } 844 | 845 | void PngWolf::log_analysis() { 846 | 847 | fprintf(stdout, "---\n" 848 | "# %u x %u pixels at depth %u (mode %u) with IDAT %u bytes (%u deflated)\n", 849 | ihdr.width, ihdr.height, ihdr.depth, ihdr.color, 850 | (unsigned int) original_inflated.size(), 851 | (unsigned int) original_deflated.size()); 852 | 853 | if (!verbose_analysis) 854 | return; 855 | 856 | fprintf(stdout, "" 857 | "image file path: %s\n" 858 | "width in pixels: %u\n" 859 | "height in pixels: %u\n" 860 | "color mode: %u\n" 861 | "color bit depth: %u\n" 862 | "interlaced: %u\n" 863 | "scanline width: %u\n" 864 | "scanline delta: %u\n" 865 | "inflated idat size: %u\n" 866 | "deflated idat size: %u\n" 867 | "chunks present: ", 868 | this->in_path, 869 | this->ihdr.width, 870 | this->ihdr.height, 871 | this->ihdr.color, 872 | this->ihdr.depth, 873 | this->ihdr.interlace, 874 | (unsigned int) this->scanline_width, 875 | (unsigned int) this->scanline_delta, 876 | (unsigned int) this->original_inflated.size(), 877 | (unsigned int) this->original_deflated.size()); 878 | 879 | for (const auto& chunk : chunks) { 880 | // TODO: check that this is broken on bad endianess systems 881 | // Also, since no validation is performed for the types, it 882 | // is possible to break the YAML output with bad files, but 883 | // that does not seem all that important at the moment. 884 | fprintf(stdout, "%c", (chunk.type >> 24)); 885 | fprintf(stdout, "%c", (chunk.type >> 16)); 886 | fprintf(stdout, "%c", (chunk.type >> 8)); 887 | fprintf(stdout, "%c", (chunk.type >> 0)); 888 | fprintf(stdout, " "); 889 | } 890 | 891 | if (ihdr.color == 6 && ihdr.depth == 8) { 892 | fprintf(stdout, "\ninvisible colors:\n"); 893 | uint32_t total = 0; 894 | 895 | // TODO: htonl is probably not right here 896 | for (const auto& it : invis_colors) { 897 | fprintf(stdout, " - %08X # %u times\n", 898 | (unsigned int) htonl(it.first), (unsigned int) it.second); 899 | total += it.second; 900 | } 901 | 902 | bool skip = invis_colors.size() == 1 903 | && invis_colors.begin()->first == 0x00000000; 904 | 905 | fprintf(stdout, " # %u pixels (%0.2f%%) are fully transparent\n", 906 | total, (double)total / ((double)ihdr.width * (double)ihdr.height)); 907 | 908 | if (invis_colors.size() > 0 && !skip) 909 | fprintf(stdout, " # --normalize-alpha changes them into transparent black\n"); 910 | } else { 911 | fprintf(stdout, "\n"); 912 | } 913 | 914 | fprintf(stdout, "" 915 | "deflated idat sizes:\n" 916 | " original filter: %0.0f\n" 917 | " none: %0.0f\n" 918 | " sub: %0.0f\n" 919 | " up: %0.0f\n" 920 | " avg: %0.0f\n" 921 | " paeth: %0.0f\n" 922 | " deflate scanline: %0.0f\n" 923 | " distinct bytes: %0.0f\n" 924 | " distinct bigrams: %0.0f\n" 925 | " incremental: %0.0f\n" 926 | " basic heuristic: %0.0f\n", 927 | this->genomes["original"]->score(), 928 | this->genomes["all set to none"]->score(), 929 | this->genomes["all set to sub"]->score(), 930 | this->genomes["all set to up"]->score(), 931 | this->genomes["all set to avg"]->score(), 932 | this->genomes["all set to paeth"]->score(), 933 | this->genomes["deflate scanline"]->score(), 934 | this->genomes["distinct bytes"]->score(), 935 | this->genomes["distinct bigrams"]->score(), 936 | this->genomes["incremental"]->score(), 937 | this->genomes["heuristic"]->score()); 938 | 939 | fprintf(stdout, "original filters:"); 940 | log_genome(*this->genomes["original"]); 941 | fprintf(stdout, "basic heuristic filters:"); 942 | log_genome(*this->genomes["heuristic"]); 943 | fprintf(stdout, "deflate scanline filters:"); 944 | log_genome(*this->genomes["deflate scanline"]); 945 | fprintf(stdout, "distinct bytes filters:"); 946 | log_genome(*this->genomes["distinct bytes"]); 947 | fprintf(stdout, "distinct bigrams filters:"); 948 | log_genome(*this->genomes["distinct bigrams"]); 949 | fprintf(stdout, "incremental filters:"); 950 | log_genome(*this->genomes["incremental"]); 951 | 952 | fflush(stdout); 953 | } 954 | 955 | void PngWolf::log_critter(const PngFilterGenome& curr_best) { 956 | const PngFilterGenome& prev_best = *best_genomes.back(); 957 | 958 | if (!this->verbose_genomes) { 959 | fprintf(stdout, "" 960 | "- deflated idat size: %7u # %+5d bytes %+4.0f seconds\n", 961 | unsigned(curr_best.score()), 962 | signed(curr_best.score() - initial_pop.best().score()), 963 | difftime(time(NULL), program_begun_at)); 964 | return; 965 | } 966 | 967 | fprintf(stdout, "" 968 | " #####################################################################\n" 969 | "- deflated idat size: %7u # %+5d bytes %+4.0f seconds since previous\n" 970 | " #####################################################################\n" 971 | " deflated bytes since previous improvement: %+d\n" 972 | " deflated bytes since first generation: %+d\n" 973 | " seconds since program launch: %+0.0f\n" 974 | " seconds since previous improvement: %+0.0f\n" 975 | " current generation is the nth: %u\n" 976 | " number of genomes evaluated: %u\n" 977 | " best filters so far:", 978 | unsigned(curr_best.score()), 979 | signed(curr_best.score() - prev_best.score()), 980 | difftime(time(NULL), last_improvement_at), 981 | signed(curr_best.score() - prev_best.score()), 982 | signed(curr_best.score() - best_genomes.front()->score()), 983 | difftime(time(NULL), program_begun_at), 984 | difftime(time(NULL), last_improvement_at), 985 | nth_generation, 986 | genomes_evaluated); 987 | 988 | log_genome(curr_best); 989 | fflush(stdout); 990 | } 991 | 992 | void PngWolf::init_filters() { 993 | 994 | GAAlleleSet allele(None, Paeth); 995 | PngFilterGenome ge(ihdr.height, allele, Evaluator); 996 | 997 | // Copy the Urcritter to all the critters we want to hold 998 | // on to for anlysis and for the initial population. 999 | 1000 | // TODO: Can clone fail? What do we do then? 1001 | 1002 | genomes["all set to avg"] = std::unique_ptr(static_cast(ge.clone())); 1003 | genomes["all set to none"] = std::unique_ptr(static_cast(ge.clone())); 1004 | genomes["all set to sub"] = std::unique_ptr(static_cast(ge.clone())); 1005 | genomes["all set to up"] = std::unique_ptr(static_cast(ge.clone())); 1006 | genomes["all set to paeth"] = std::unique_ptr(static_cast(ge.clone())); 1007 | genomes["original"] = std::unique_ptr(static_cast(ge.clone())); 1008 | genomes["heuristic"] = std::unique_ptr(static_cast(ge.clone())); 1009 | genomes["deflate scanline"] = std::unique_ptr(static_cast(ge.clone())); 1010 | genomes["distinct bytes"] = std::unique_ptr(static_cast(ge.clone())); 1011 | genomes["distinct bigrams"] = std::unique_ptr(static_cast(ge.clone())); 1012 | genomes["incremental"] = std::unique_ptr(static_cast(ge.clone())); 1013 | 1014 | for (int i = 0; i < ge.size(); ++i) { 1015 | genomes["original"]->gene(i, original_filters[i]); 1016 | genomes["all set to avg"]->gene(i, Avg); 1017 | genomes["all set to sub"]->gene(i, Sub); 1018 | genomes["all set to none"]->gene(i, None); 1019 | genomes["all set to paeth"]->gene(i, Paeth); 1020 | genomes["all set to up"]->gene(i, Up); 1021 | } 1022 | 1023 | flt_singles[None] = refilter(*genomes["all set to none"]); 1024 | flt_singles[Sub] = refilter(*genomes["all set to sub"]); 1025 | flt_singles[Up] = refilter(*genomes["all set to up"]); 1026 | flt_singles[Avg] = refilter(*genomes["all set to avg"]); 1027 | flt_singles[Paeth] = refilter(*genomes["all set to paeth"]); 1028 | 1029 | // TODO: for bigger_is_better it might make sense to have a 1030 | // function for the comparisons and set that so heuristics 1031 | // also work towards making the selection worse. 1032 | 1033 | for (int row = 0; row < ge.size(); ++row) { 1034 | size_t best_sum = SIZE_MAX; 1035 | PngFilter best_flt = None; 1036 | 1037 | // "The following simple heuristic has performed well in 1038 | // early tests: compute the output scanline using all five 1039 | // filters, and select the filter that gives the smallest 1040 | // sum of absolute values of outputs. (Consider the output 1041 | // bytes as signed differences for this test.) This method 1042 | // usually outperforms any single fixed filter choice." as 1043 | // per . 1044 | 1045 | // Note that I've found this to be incorrect, as far as 1046 | // typical RGB and RGBA images found on the web go, using 1047 | // None for all scanlines outperforms the heuristic in 57% 1048 | // of the cases. Even if you carefully check whether they 1049 | // should really be stored as indexed images, there is not 1050 | // much evidence to support "usually". A better heuristic 1051 | // would be applying the heuristic and None to all and use 1052 | // the combination that performs better. 1053 | 1054 | for (const auto& fi : flt_singles) { 1055 | std::vector::iterator scanline = 1056 | flt_singles[fi.first].begin() + row * scanline_width; 1057 | 1058 | size_t sum = std::accumulate(scanline + 1, 1059 | scanline + scanline_width, 0, sum_abs); 1060 | 1061 | // If, for this scanline, the current filter is better 1062 | // then the previous best filter, we memorize this filter, 1063 | // otherwise this filter can be disregarded for the line. 1064 | if (sum >= best_sum) 1065 | continue; 1066 | 1067 | best_sum = sum; 1068 | best_flt = fi.first; 1069 | } 1070 | 1071 | genomes["heuristic"]->gene(row, (PngFilter)best_flt); 1072 | } 1073 | 1074 | // As an experimental heuristic, this compresses each scanline 1075 | // individually and picks the filter that compresses the line 1076 | // best. This may be a useful clue for the others, but tests 1077 | // suggests this might interfere in cases where zlib is a poor 1078 | // estimator, tuning genomes too much for zlib instead of Zopfli. 1079 | // Generally this should be expected to perform poorly for very 1080 | // small images. In the standard Alexa 1000 sample it performs 1081 | // better than the specification's heuristic in 73% of cases; 1082 | // files would be around 3% (median) and 4% (mean) smaller. 1083 | 1084 | for (int row = 0; row < ge.size(); ++row) { 1085 | size_t best_sum = SIZE_MAX; 1086 | PngFilter best_flt = None; 1087 | 1088 | for (const auto& fi : flt_singles) { 1089 | std::vector::iterator scanline = 1090 | flt_singles[fi.first].begin() + row * scanline_width; 1091 | 1092 | std::vector line(scanline, scanline + scanline_width); 1093 | size_t sum = deflate_estimator->deflate(line).size(); 1094 | 1095 | if (sum >= best_sum) 1096 | continue; 1097 | 1098 | best_sum = sum; 1099 | best_flt = fi.first; 1100 | } 1101 | 1102 | genomes["deflate scanline"]->gene(row, (PngFilter)best_flt); 1103 | } 1104 | 1105 | // unigram heuristic 1106 | for (int row = 0; row < ge.size(); ++row) { 1107 | size_t best_sum = SIZE_MAX; 1108 | PngFilter best_flt = None; 1109 | 1110 | for (const auto& fi : flt_singles) { 1111 | std::bitset<65536> seen; 1112 | std::vector::iterator it; 1113 | std::vector::iterator scanline = 1114 | flt_singles[fi.first].begin() + row * scanline_width; 1115 | 1116 | for (it = scanline; it < scanline + scanline_width; ++it) 1117 | seen.set(uint8_t(*it)); 1118 | 1119 | size_t sum = seen.count(); 1120 | 1121 | if (sum >= best_sum) 1122 | continue; 1123 | 1124 | best_sum = sum; 1125 | best_flt = fi.first; 1126 | } 1127 | 1128 | genomes["distinct bytes"]->gene(row, (PngFilter)best_flt); 1129 | } 1130 | 1131 | // bigram heuristic 1132 | for (int row = 0; row < ge.size(); ++row) { 1133 | size_t best_sum = SIZE_MAX; 1134 | PngFilter best_flt = None; 1135 | 1136 | for (const auto& fi : flt_singles) { 1137 | std::bitset<65536> seen; 1138 | std::vector::iterator it; 1139 | std::vector::iterator scanline = 1140 | flt_singles[fi.first].begin() + row * scanline_width; 1141 | 1142 | for (it = scanline + 1; it < scanline + scanline_width; ++it) 1143 | seen.set((uint8_t(*(it - 1)) << 8) | uint8_t(*it)); 1144 | 1145 | size_t sum = seen.count(); 1146 | 1147 | if (sum >= best_sum) 1148 | continue; 1149 | 1150 | best_sum = sum; 1151 | best_flt = fi.first; 1152 | } 1153 | 1154 | genomes["distinct bigrams"]->gene(row, (PngFilter)best_flt); 1155 | } 1156 | 1157 | z_stream strm; 1158 | strm.zalloc = Z_NULL; 1159 | strm.zfree = Z_NULL; 1160 | strm.opaque = Z_NULL; 1161 | 1162 | if (deflateInit(&strm, wolf.zlib_level) != Z_OK) { 1163 | abort(); 1164 | } 1165 | 1166 | size_t max = deflateBound(&strm, original_inflated.size()); 1167 | std::vector strm_deflated(max); 1168 | strm.next_out = strm_deflated.data(); 1169 | strm.avail_out = strm_deflated.size(); 1170 | 1171 | for (int row = 0; row < ge.size(); ++row) { 1172 | size_t pos = row * scanline_width; 1173 | size_t best_sum = INT_MAX; 1174 | PngFilter best_flt = None; 1175 | 1176 | for (const auto& fi : flt_singles) { 1177 | z_stream here; 1178 | 1179 | if (deflateCopy(&here, &strm) != Z_OK) { 1180 | } 1181 | 1182 | here.next_in = &flt_singles[fi.first][pos]; 1183 | here.avail_in = scanline_width; 1184 | 1185 | int status = deflate(&here, Z_FINISH); 1186 | if (status != Z_STREAM_END && status != Z_OK) { 1187 | } 1188 | 1189 | size_t sum = max - here.avail_out; 1190 | 1191 | deflateEnd(&here); 1192 | 1193 | if (sum >= best_sum) 1194 | continue; 1195 | 1196 | best_sum = sum; 1197 | best_flt = fi.first; 1198 | } 1199 | 1200 | genomes["incremental"]->gene(row, (PngFilter)best_flt); 1201 | strm.next_in = &flt_singles[(PngFilter)best_flt][pos]; 1202 | strm.avail_in = scanline_width; 1203 | 1204 | if (deflate(&strm, Z_NO_FLUSH) != Z_OK) { 1205 | } 1206 | 1207 | } 1208 | 1209 | deflateEnd(&strm); 1210 | 1211 | // As initial population this uses, by default, the filters in the 1212 | // original image, the filters derived by the heuristic proposed by 1213 | // the PNG specification, the unary filter selections where every 1214 | // scanline uses the same filter, and a couple of random ones re- 1215 | // required to fill the population to the requested size. With some 1216 | // Genetic Algorithms results vary a lot depending on the filters 1217 | // in the initial population. For instance, improvements are hard to 1218 | // find with some GAs if the heuristic filter selection is included. 1219 | 1220 | for (auto&& genome : genomes) 1221 | genome.second->evaluate(); 1222 | 1223 | if (!exclude_singles) { 1224 | initial_pop.add(*this->genomes["all set to none"]); 1225 | initial_pop.add(*this->genomes["all set to sub"]); 1226 | initial_pop.add(*this->genomes["all set to up"]); 1227 | initial_pop.add(*this->genomes["all set to avg"]); 1228 | initial_pop.add(*this->genomes["all set to paeth"]); 1229 | } 1230 | 1231 | if (!exclude_original) 1232 | initial_pop.add(*this->genomes["original"]); 1233 | 1234 | if (!exclude_heuristic) 1235 | initial_pop.add(*this->genomes["heuristic"]); 1236 | 1237 | if (!exclude_experiment1) 1238 | initial_pop.add(*this->genomes["deflate scanline"]); 1239 | 1240 | if (!exclude_experiment2) 1241 | initial_pop.add(*this->genomes["distinct bytes"]); 1242 | 1243 | if (!exclude_experiment3) 1244 | initial_pop.add(*this->genomes["distinct bigrams"]); 1245 | 1246 | if (!exclude_experiment4) 1247 | initial_pop.add(*this->genomes["incremental"]); 1248 | 1249 | // If all standard genomes have been excluded a randomized one has 1250 | // to be added so the population knows how to make more genomes. 1251 | if (initial_pop.size() == 0) { 1252 | PngFilterGenome clone(*genomes["original"]); 1253 | clone.initialize(); 1254 | initial_pop.add(clone); 1255 | } 1256 | 1257 | // This adds random critters to the initial population. Very low 1258 | // values for the population size generally lead to insufficient 1259 | // genetic diversity so improvements are rarely found, while with 1260 | // very high values evolution takes too much time. I've found the 1261 | // value here works okay-ish for reasonably sized images. There 1262 | // is the option to make this configurable, but there is not much 1263 | // evidence the value makes that much of a difference. 1264 | initial_pop.size(population_size); 1265 | 1266 | // This defines ordering by score (idat size in our case). Lower 1267 | // idat size is better than higher idat size, setting accordingly. 1268 | initial_pop.order(GAPopulation::LOW_IS_BEST); 1269 | 1270 | if (bigger_is_better) 1271 | initial_pop.order(GAPopulation::HIGH_IS_BEST); 1272 | } 1273 | 1274 | //////////////////////////////////////////////////////////////////// 1275 | // Experiment 1276 | //////////////////////////////////////////////////////////////////// 1277 | void PngWolf::run() { 1278 | 1279 | // With what few samples I have used in testing, GAIncrementalGA 1280 | // works very well with the other options I've used, it finds im- 1281 | // provements fairly reliably while other Algorithms have trouble 1282 | // to find improvements after a certain number of generations. 1283 | GAIncrementalGA ga(initial_pop); 1284 | 1285 | // There is no particular reason to use the tournament selector, 1286 | // but the general concept seems sound for our purposes here, and 1287 | // I've found this to work better than some others in my simple 1288 | // tests, better than selecting by Rank for instance. 1289 | ga.selector(GATournamentSelector()); 1290 | 1291 | // I am not entirely sure I understand the scaling concept in the 1292 | // GALib library, I initially did not realize fitness and score 1293 | // weren't the same thing in GALib, so I turned scaling off to 1294 | // make "fitness" the same as the "score". After gaining a better 1295 | // understanding of "scaling" I tried a couple of other things, 1296 | // but no scaling still seemed to work best for my samples. 1297 | ga.scaling(GANoScaling()); 1298 | 1299 | // This is currently not used and maybe should not be used as the 1300 | // scoping slash ownership is unclear. It would work only during 1301 | // run() which is a bit non-intuitive, so TODO: maybe remove this. 1302 | this->ga = &ga; 1303 | 1304 | ga.crossover(PngFilterGenome::TwoPointCrossover); 1305 | 1306 | best_genomes.push_back(std::unique_ptr( 1307 | static_cast(ga.population().best().clone()))); 1308 | 1309 | fprintf(stdout, "---\n"); 1310 | 1311 | if (ihdr.height == 1) 1312 | goto after_while; 1313 | 1314 | while (!should_abort) { 1315 | 1316 | double since_start = difftime(time(NULL), program_begun_at); 1317 | double since_last = difftime(time(NULL), last_improvement_at); 1318 | size_t deflated = genomes_evaluated * original_inflated.size(); 1319 | 1320 | if (max_time > 0 && max_time < since_start) 1321 | break; 1322 | 1323 | if (max_evaluations > 0 && max_evaluations < genomes_evaluated) 1324 | break; 1325 | 1326 | if (max_stagnate_time > 0 && max_stagnate_time < since_last) 1327 | break; 1328 | 1329 | if (max_deflate > 0 && max_deflate < deflated / (1024*1024)) 1330 | break; 1331 | 1332 | nth_generation++; 1333 | 1334 | ga.step(); 1335 | last_step_at = time(NULL); 1336 | 1337 | if (should_abort) 1338 | break; 1339 | 1340 | PngFilterGenome& new_best = 1341 | (PngFilterGenome&)ga.population().best(); 1342 | 1343 | if (!bigger_is_better) 1344 | if (new_best.score() >= best_genomes.back()->score()) 1345 | continue; 1346 | 1347 | if (bigger_is_better) 1348 | if (new_best.score() <= best_genomes.back()->score()) 1349 | continue; 1350 | 1351 | log_critter(new_best); 1352 | 1353 | last_improvement_at = time(NULL); 1354 | best_genomes.push_back(std::unique_ptr(static_cast(new_best.clone()))); 1355 | } 1356 | 1357 | after_while: 1358 | this->ga = nullptr; 1359 | 1360 | // Since we intercept CTRL+C the user should get some feedback 1361 | // on that as soon as possible, so the log header comes here. 1362 | fprintf(stdout, "---\n"); 1363 | fflush(stdout); 1364 | } 1365 | 1366 | void PngWolf::recompress() { 1367 | best_inflated = refilter(*best_genomes.back()); 1368 | best_deflated = deflate_out->deflate(best_inflated); 1369 | 1370 | // In my test sample in 1.66% of cases, using a high zlib level, 1371 | // zlib is able to produce smaller output than Zopfli. So for the 1372 | // case where users do choose a high setting for zlib, reward 1373 | // them by using zlib instead to recompress. Since zlib is fast, 1374 | // this recompression should not be much of a performance hit. 1375 | 1376 | // TODO: This should be noted in the verbose output, otherwise 1377 | // this would make Zopfli appear better than it is. In the longer 1378 | // term perhaps the output should simply say what estimator and 1379 | // what compressor was used and give the respective sizes. 1380 | 1381 | if (best_deflated.size() > best_genomes.back()->score()) { 1382 | best_deflated = deflate_estimator->deflate(best_inflated); 1383 | } 1384 | 1385 | // TODO: Doing this here is a bit of an hack, and doing it 1386 | // should also be logged in the verbose output. Main problem 1387 | // is separation of things you'd put into a library and what 1388 | // is really more part of the command line application. Right 1389 | // now run() should really do this, but then you could not 1390 | // abort Zopfli easily. Also not sure what --best-idat-to ought 1391 | // to do here. Might end up exposing a step() method and let 1392 | // the command line part do logging and other things. 1393 | 1394 | if (best_deflated.size() > original_deflated.size() && !even_if_bigger) { 1395 | best_genomes.push_back(std::unique_ptr(genomes["original"].release())); 1396 | genomes.erase("original"); 1397 | best_inflated = original_inflated; 1398 | best_deflated = original_deflated; 1399 | } 1400 | 1401 | done_deflating_at = time(NULL); 1402 | } 1403 | 1404 | bool PngWolf::read_file() { 1405 | 1406 | char fileMagic[8]; 1407 | unsigned int iend_chunk_count = 0; 1408 | size_t expected; 1409 | 1410 | std::ifstream in; 1411 | in.exceptions(std::ios::badbit | std::ios::failbit); 1412 | in.open(in_path, std::ios::binary | std::ios::in); 1413 | in.read(fileMagic, 8); 1414 | 1415 | if (memcmp(fileMagic, PNG_MAGIC, 8) != 0) 1416 | goto error; 1417 | 1418 | while (!in.eof()) { 1419 | PngChunk chunk; 1420 | 1421 | in.read((char*)&chunk.size, sizeof(chunk.size)); 1422 | chunk.size = ntohl(chunk.size); 1423 | 1424 | in.read((char*)&chunk.type, sizeof(chunk.type)); 1425 | chunk.type = ntohl(chunk.type); 1426 | 1427 | if (chunk.size > 0) { 1428 | chunk.data.resize(chunk.size); 1429 | in.read((char*)&chunk.data[0], chunk.size); 1430 | } 1431 | 1432 | in.read((char*)&chunk.crc32, sizeof(chunk.crc32)); 1433 | 1434 | chunk.crc32 = ntohl(chunk.crc32); 1435 | 1436 | // IHDR 1437 | if (chunk.type == IHDR_TYPE && chunk.size == 13) { 1438 | 1439 | // TODO: This does not check that this is the first and only 1440 | // IHDR chunk in the file even though only one IHDR is allowed. 1441 | 1442 | memcpy(&ihdr.width, &chunk.data.at(0), sizeof(ihdr.width)); 1443 | ihdr.width = ntohl(ihdr.width); 1444 | 1445 | memcpy(&ihdr.height, &chunk.data.at(4), sizeof(ihdr.height)); 1446 | ihdr.height = ntohl(ihdr.height); 1447 | 1448 | ihdr.depth = chunk.data.at(8); 1449 | ihdr.color = chunk.data.at(9); 1450 | ihdr.comp = chunk.data.at(10); 1451 | ihdr.filter = chunk.data.at(11); 1452 | ihdr.interlace = chunk.data.at(12); 1453 | } 1454 | 1455 | // IDAT 1456 | if (chunk.type == IDAT_TYPE) 1457 | original_deflated.insert(original_deflated.end(), 1458 | chunk.data.begin(), chunk.data.end()); 1459 | 1460 | // IEND 1461 | if (chunk.type == IEND_TYPE) 1462 | iend_chunk_count++; 1463 | 1464 | chunks.push_back(chunk); 1465 | 1466 | // Peek so the eof check works as expected. 1467 | in.peek(); 1468 | } 1469 | 1470 | in.close(); 1471 | 1472 | // We can't do anything if there is no image data in the input. 1473 | if (original_deflated.size() == 0) 1474 | goto error; 1475 | 1476 | // For simplicity, we rely on the image having only exactly one 1477 | // IEND chunk (as mandated by the specification) so inserting a 1478 | // new IDAT chunk is simple. Note that there are other possible 1479 | // errors with the chunk arrangement that are not checked for, 1480 | // but the worst that would happen is that a broken image is re- 1481 | // written into a new similarily broken image, which is fine. 1482 | if (iend_chunk_count != 1) 1483 | goto error; 1484 | 1485 | // PNG does not allow images with zero height or width, and at 1486 | // the time of writing, only filter mode zero was permitted. 1487 | if (ihdr.width == 0 || ihdr.height == 0 || ihdr.filter != 0) 1488 | goto error; 1489 | 1490 | // At the time of writing, compression level zero was the only 1491 | // valid one, and color modes could not exceed six; interlaced 1492 | // images are not supported, mainly because the author did not 1493 | // bother to implement Adam7 when the goal is to minimize size. 1494 | // Futher checks on the color mode are performed later. TODO: 1495 | // since interlaced images are not supported, that may merit a 1496 | // specific error message pointing that design decision out. 1497 | if (ihdr.comp != 0 || ihdr.interlace != 0 || ihdr.color > 6) 1498 | goto error; 1499 | 1500 | // PNG does not allow bit depths below one or above 16 1501 | if (ihdr.depth == 0 || ihdr.depth > 16) 1502 | goto error; 1503 | 1504 | // PNG bit depths must be a power of two 1505 | if ((ihdr.depth - 1) & ihdr.depth) 1506 | goto error; 1507 | 1508 | static const uint32_t channel_map[] = { 1509 | 1, 0, 3, 1, 2, 0, 4 1510 | }; 1511 | 1512 | // This validates generally permissable color modes. It does not 1513 | // fully check whether the combination of bith depth and color 1514 | // mode is permitted by the specification. TODO: Maybe it should. 1515 | if (channel_map[ihdr.color] < 1) 1516 | goto error; 1517 | 1518 | original_inflated = inflate_zlib(original_deflated); 1519 | 1520 | expected = ihdr.height * int(ceil( 1521 | float(((ihdr.width * channel_map[ihdr.color] * ihdr.depth + 8) / 8.0f)) 1522 | )); 1523 | 1524 | if (expected != original_inflated.size()) 1525 | goto error; 1526 | 1527 | scanline_width = expected / ihdr.height; 1528 | scanline_delta = channel_map[ihdr.color] * 1529 | int(ceil(float(ihdr.depth / 8.0))); 1530 | 1531 | for (size_t ix = 0; ix < ihdr.height; ++ix) { 1532 | unsigned char filter = original_inflated.at(ix * scanline_width); 1533 | 1534 | // Abort when the image uses an unsupported filter type 1535 | if (filter > 4) 1536 | goto error; 1537 | 1538 | original_filters.push_back((PngFilter)filter); 1539 | } 1540 | 1541 | // TODO: copy properly here 1542 | original_unfiltered.resize(original_inflated.size()); 1543 | memcpy(original_unfiltered.data(), 1544 | original_inflated.data(), original_inflated.size()); 1545 | 1546 | unfilter_idat(original_unfiltered.data(), 1547 | ihdr.height, scanline_delta, scanline_width); 1548 | 1549 | // Creates a histogram of the fully transparent pixels in the 1550 | // image. It is apparently common for graphics programs to 1551 | // keep the color values of fully transparent pixels around, 1552 | // but this is rarely desired and makes compression harder, so 1553 | // we tell users about that and offer to normalize the pixels. 1554 | 1555 | // TODO: Now that it is modified, original_unfiltered is the 1556 | // wrong name for the attribute. 1557 | 1558 | if (ihdr.color == 6 && ihdr.depth == 8) { 1559 | uint32_t pixel; 1560 | uint32_t zero = 0; 1561 | for (uint32_t row = 0; row < ihdr.height; ++row) { 1562 | for (uint32_t col = 1; col < scanline_width; col += 4) { 1563 | size_t pos = row * scanline_width + col; 1564 | 1565 | if (original_unfiltered[pos + 3] != 0) 1566 | continue; 1567 | 1568 | memcpy(&pixel, &original_unfiltered[pos], 4); 1569 | invis_colors[pixel]++; 1570 | 1571 | if (normalize_alpha) 1572 | memcpy(&original_unfiltered[pos], &zero, 4); 1573 | } 1574 | } 1575 | } 1576 | 1577 | return false; 1578 | 1579 | error: 1580 | return true; 1581 | } 1582 | 1583 | //////////////////////////////////////////////////////////////////// 1584 | // Save data 1585 | //////////////////////////////////////////////////////////////////// 1586 | bool PngWolf::save_original_idat(const char* path) { 1587 | return save_idat(path, original_deflated, original_inflated); 1588 | } 1589 | 1590 | bool PngWolf::save_best_idat(const char* path) { 1591 | return save_idat(path, best_deflated, best_inflated); 1592 | } 1593 | 1594 | bool PngWolf::save_idat(const char* path, std::vector& deflated, std::vector& inflated) { 1595 | 1596 | // TODO: when there is a simple inflate() function, make this 1597 | // assert when deflated and inflated to not mach each other. 1598 | // Or alternatively make some IDAT struct that has both and 1599 | // then require its use for this function. 1600 | 1601 | FILE* out = fopen(path, "wb"); 1602 | 1603 | static const uint8_t GZIP_HEADER[] = { 1604 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03 1605 | }; 1606 | 1607 | if (out == NULL) 1608 | return true; 1609 | 1610 | if (fwrite(GZIP_HEADER, sizeof(GZIP_HEADER), 1, out) != 1) { 1611 | } 1612 | 1613 | if (fwrite(&deflated[2], deflated.size() - 6, 1, out) != 1) { 1614 | } 1615 | 1616 | // TODO: endianess? 1617 | 1618 | uint32_t crc = crc32(0L, Z_NULL, 0); 1619 | crc = crc32(crc, inflated.data(), inflated.size()); 1620 | 1621 | if (fwrite(&crc, sizeof(crc), 1, out) != 1) { 1622 | } 1623 | 1624 | uint32_t size = inflated.size(); 1625 | if (fwrite(&size, sizeof(size), 1, out) != 1) { 1626 | } 1627 | 1628 | if (fclose(out) != 0) { 1629 | } 1630 | 1631 | return false; 1632 | } 1633 | 1634 | bool PngWolf::recompress_optional_chunk(PngChunk &chunk) { 1635 | if (chunk.type == iCCP_TYPE) { 1636 | std::vector::iterator i; 1637 | 1638 | // Find null seperator at end of profile name 1639 | i = find(chunk.data.begin(), chunk.data.end(), 0); 1640 | 1641 | if (chunk.data.end() - i < 3) { 1642 | return true; 1643 | } 1644 | 1645 | ++i; 1646 | 1647 | // Check compression method is 0 1648 | if (*i != 0) { 1649 | return true; 1650 | } 1651 | 1652 | ++i; 1653 | 1654 | std::vector original_deflated(i, chunk.data.end()); 1655 | 1656 | std::vector inflated_data(inflate_zlib(original_deflated)); 1657 | 1658 | std::vector deflated_data(deflate_out->deflate(inflated_data)); 1659 | 1660 | if (deflated_data.size() < original_deflated.size()) { 1661 | chunk.data.erase(i, chunk.data.end()); 1662 | chunk.data.insert(chunk.data.end(), 1663 | deflated_data.begin(), deflated_data.end()); 1664 | chunk.size = chunk.data.size(); 1665 | 1666 | update_chunk_crc(chunk); 1667 | 1668 | printf("# iCCP %u bytes smaller\n", 1669 | (unsigned int) (original_deflated.size() - deflated_data.size())); 1670 | 1671 | return false; 1672 | } 1673 | } 1674 | 1675 | return true; 1676 | } 1677 | 1678 | bool PngWolf::save_file() { 1679 | 1680 | // Create new list of chunks with old IDATs removed, and when 1681 | // IEND is seen, insert a new IDAT chunk and the IEND chunk. 1682 | // Then proceed with serializing the whole new list to a file. 1683 | 1684 | std::list new_chunks; 1685 | 1686 | for (auto&& chunk : chunks) { 1687 | if (chunk.type == IDAT_TYPE) 1688 | continue; 1689 | 1690 | if (chunk.type != IEND_TYPE) { 1691 | if (chunk.type == IHDR_TYPE) { 1692 | new_chunks.push_back(chunk); 1693 | continue; 1694 | } 1695 | 1696 | if (chunk.type == PLTE_TYPE && ihdr.color == 3) { 1697 | new_chunks.push_back(chunk); 1698 | continue; 1699 | } 1700 | 1701 | auto i = find(strip_chunks.begin(), strip_chunks.end(), chunk.type); 1702 | 1703 | if (i != strip_chunks.end()) { 1704 | continue; 1705 | } 1706 | 1707 | if (!strip_optional) { 1708 | recompress_optional_chunk(chunk); 1709 | new_chunks.push_back(chunk); 1710 | continue; 1711 | } 1712 | 1713 | if (chunk.type == tRNS_TYPE) { 1714 | new_chunks.push_back(chunk); 1715 | continue; 1716 | } 1717 | 1718 | i = find(keep_chunks.begin(), keep_chunks.end(), chunk.type); 1719 | 1720 | if (i != keep_chunks.end()) { 1721 | recompress_optional_chunk(chunk); 1722 | new_chunks.push_back(chunk); 1723 | } 1724 | 1725 | continue; 1726 | } 1727 | 1728 | PngChunk new_idat; 1729 | new_idat.type = IDAT_TYPE; 1730 | new_idat.data = best_deflated; 1731 | new_idat.size = new_idat.data.size(); 1732 | 1733 | update_chunk_crc(new_idat); 1734 | 1735 | new_chunks.push_back(new_idat); 1736 | new_chunks.push_back(chunk); 1737 | } 1738 | 1739 | // TODO: it might be nice to not overwrite existing files, but 1740 | // as a rule, if there are separate parameters for in and out, 1741 | // that might not provide the best usability for users. 1742 | 1743 | FILE* out = fopen(out_path, "wb"); 1744 | 1745 | if (out == NULL) 1746 | return true; 1747 | 1748 | if (fwrite(PNG_MAGIC, 8, 1, out) != 1) { 1749 | } 1750 | 1751 | for (const auto& chunk : new_chunks) { 1752 | uint32_t size = htonl(chunk.size); 1753 | uint32_t type = htonl(chunk.type); 1754 | uint32_t crc32 = htonl(chunk.crc32); 1755 | 1756 | // TODO: does this merit handling write errors? 1757 | 1758 | if (fwrite(&size, sizeof(size), 1, out) != 1) { 1759 | } 1760 | 1761 | if (fwrite(&type, sizeof(type), 1, out) != 1) { 1762 | } 1763 | 1764 | if (chunk.data.size() > 0) { 1765 | if (fwrite(chunk.data.data(), chunk.data.size(), 1, out) != 1) { 1766 | } 1767 | } 1768 | 1769 | if (fwrite(&crc32, sizeof(crc32), 1, out) != 1) { 1770 | } 1771 | } 1772 | 1773 | if (fclose(out) != 0) { 1774 | } 1775 | 1776 | return false; 1777 | } 1778 | 1779 | std::vector split_string(const std::string& s, const std::string& delims, bool include_empty=true) { 1780 | std::vector tokens; 1781 | std::string::size_type left = 0; 1782 | 1783 | for (;;) { 1784 | auto right = s.find_first_of(delims, left); 1785 | 1786 | auto token = s.substr(left, right - left); 1787 | if (include_empty || !token.empty()) { 1788 | tokens.push_back(token); 1789 | } 1790 | 1791 | if (right == std::string::npos) { 1792 | break; 1793 | } 1794 | 1795 | left = ++right; 1796 | } 1797 | 1798 | return tokens; 1799 | } 1800 | 1801 | std::unique_ptr get_deflater_from_option(const std::string& s) { 1802 | std::unique_ptr res; 1803 | 1804 | auto i = s.find(','); 1805 | 1806 | if (s.compare(0, i, "libdeflate") == 0) { 1807 | res = std::make_unique(); 1808 | } else if (s.compare(0, i, "zlib") == 0) { 1809 | res = std::make_unique(); 1810 | } else if (s.compare(0, i, "zopfli") == 0) { 1811 | res = std::make_unique(); 1812 | } else { 1813 | fprintf(stderr, "pngwolf: unknown deflater '%s'\n", s.substr(0, i).c_str()); 1814 | return nullptr; 1815 | } 1816 | 1817 | if (i == std::string::npos) { 1818 | return res; 1819 | } 1820 | 1821 | for (const auto& option : split_string(std::string(s, i + 1), ",")) { 1822 | if (!res->parse_option(option)) { 1823 | fprintf(stderr, "pngwolf: %s option error at '%s'\n", s.substr(0, i).c_str(), option.c_str()); 1824 | return nullptr; 1825 | } 1826 | } 1827 | 1828 | return res; 1829 | } 1830 | 1831 | //////////////////////////////////////////////////////////////////// 1832 | // Help! 1833 | //////////////////////////////////////////////////////////////////// 1834 | void show_help(void) { 1835 | fprintf(stdout, "%s", 1836 | " -----------------------------------------------------------------------------\n" 1837 | " Usage: pngwolf --in=file.png --out=file.png \n" 1838 | " -----------------------------------------------------------------------------\n" 1839 | " --in= The PNG input image \n" 1840 | " --out= The PNG output file (defaults to not saving!)\n" 1841 | " --original-idat-to= Save original IDAT data in a gzip container \n" 1842 | " --best-idat-to= Save best IDAT data in a gzip container \n" 1843 | " --out-deflate= Lib for output (libdeflate, zlib, zopfli) \n" 1844 | " \n" 1845 | "Evaluation options: \n" 1846 | " --estimator= Lib for estimator (libdeflate, zlib, zopfli) \n" 1847 | " --exclude-singles Exclude single-filter genomes from population\n" 1848 | " --exclude-original Exclude the filters of the input image \n" 1849 | " --exclude-heuristic Exclude the heuristically generated filters \n" 1850 | " --exclude-experiments Exclude experimental heuristics \n" 1851 | " --population-size= Size of the population. Defaults to 19. \n" 1852 | " --max-time= Timeout after seconds. (default: 0, disabled)\n" 1853 | " --max-stagnate-time= Give up if no improvement is found (d: 5) \n" 1854 | " --max-deflate= Give up after deflating this many megabytes \n" 1855 | " --max-evaluations= Give up after evaluating this many genomes \n" 1856 | " --zlib-level= Compression level for inc. heuristic (d: 7) \n" 1857 | " \n" 1858 | "PNG options: \n" 1859 | " --normalize-alpha For RGBA, make fully transparent pixels black\n" 1860 | " --keep-chunk= Keep chunks matching name (4 chars) \n" 1861 | " --strip-chunk= Strip chunks matching name (4 chars) \n" 1862 | " --strip-optional Strip all optional chunks except tRNS \n" 1863 | " \n" 1864 | "Verbosity options: \n" 1865 | " --verbose-analysis More details in initial image analysis \n" 1866 | " --verbose-genomes More details when improvements are found \n" 1867 | " --verbose-summary More details in optimization summary \n" 1868 | " --verbose Shorthand for all verbosity options above \n" 1869 | " \n" 1870 | "Misc options: \n" 1871 | " --even-if-bigger Otherwise the original is copied if it's best\n" 1872 | " --bigger-is-better Find filter sequences that compress worse \n" 1873 | " --info Just print out verbose analysis and exit \n" 1874 | " --help Print this help page and exit \n" 1875 | " --version Print version number and exit \n" 1876 | " \n" 1877 | "The names of the estimator and output deflate libraries can optionally be \n" 1878 | "followed by comma-separated options. These options are: \n" 1879 | " libdeflate: level= Compression level (1-12, default: 4) \n" 1880 | " zlib: level= Compression level (0-9, default: 3) \n" 1881 | " strategy= Strategy (0-3, default: 0) \n" 1882 | " window= Window bits (8-15, default: 15) \n" 1883 | " memlevel= Memory level (1-9, default: 8) \n" 1884 | " zopfli: iter= Iteratons (default: 15) \n" 1885 | " maxsplit= Max blocks to split into (0=inf, default: 15)\n" 1886 | " verbose= Zopfli verbosity level (0-2, default: 0) \n" 1887 | " -----------------------------------------------------------------------------\n" 1888 | " To reduce the file size of PNG images `pngwolf` uses a genetic algorithm for \n" 1889 | " finding the best scanline filter for each scanline in the image. It does not \n" 1890 | " implement any other optimization techniques (a future version may attempt to \n" 1891 | " use a similar approach to find a good arrangement of color palette entries). \n" 1892 | " \n" 1893 | " To approximate the quality of a filter combination it compresses IDAT chunks \n" 1894 | " using an estimator and ultimately uses the `Zopfli` encoder to store the \n" 1895 | " output image. It is slow because it recompresses the IDAT data fully for all \n" 1896 | " filter combinations even if only minor changes are made or if two filter com-\n" 1897 | " binations are merged, as `zlib` has no built-in support for caching analysis \n" 1898 | " data. Send mail if you know of a freely available encoder that supports that.\n" 1899 | " -----------------------------------------------------------------------------\n" 1900 | " Output images should be saved even if you send SIGINT (~CTRL+C) to `pngwolf`.\n" 1901 | " The machine-readable progress report format is based on YAML http://yaml.org/\n" 1902 | " -----------------------------------------------------------------------------\n" 1903 | " Uses http://lancet.mit.edu/ga/ and https://github.com/ebiggers/libdeflate \n" 1904 | " and http://zlib.net/ and https://github.com/google/zopfli/ \n" 1905 | " -----------------------------------------------------------------------------\n" 1906 | " Note: This version was modified to use Zopfli for the final compression step,\n" 1907 | " https://github.com/jibsen/pngwolf-zopfli/ \n" 1908 | " -----------------------------------------------------------------------------\n" 1909 | " http://bjoern.hoehrmann.de/pngwolf/ (c) 2008-2011 http://bjoern.hoehrmann.de/\n" 1910 | ""); 1911 | } 1912 | 1913 | void show_version(void) { 1914 | fprintf(stdout, "%s", 1915 | "pngwolf-zopfli " PNGWOLF_VERSION "\n" 1916 | "\n" 1917 | "Copyright (C) 2008-2011 Bjoern Hoehrmann \n" 1918 | "\n" 1919 | "Modified to use Zopfli for the final compression step\n" 1920 | "Copyright (C) 2015-2017 Joergen Ibsen\n" 1921 | "\n" 1922 | "This is free software; see the source for copying conditions.\n" 1923 | "There is NO WARRANTY, to the extent permitted by law.\n" 1924 | ""); 1925 | } 1926 | 1927 | //////////////////////////////////////////////////////////////////// 1928 | // main 1929 | //////////////////////////////////////////////////////////////////// 1930 | int main(int argc, char* argv[]) { 1931 | 1932 | bool argVerboseAnalysis = false; 1933 | bool argVerboseSummary = false; 1934 | bool argVerboseGenomes = false; 1935 | bool argExcludeSingles = false; 1936 | bool argExcludeOriginal = false; 1937 | bool argExcludeHeuristic = false; 1938 | bool argExcludeExperiment1 = false; 1939 | bool argExcludeExperiment2 = false; 1940 | bool argExcludeExperiment3 = false; 1941 | bool argExcludeExperiment4 = false; 1942 | bool argInfo = false; 1943 | bool argNormalizeAlpha = false; 1944 | bool argEvenIfBigger = false; 1945 | bool argAutoMpass = false; 1946 | bool argBiggerIsBetter = false; 1947 | const char* argPng = NULL; 1948 | const char* argOut = NULL; 1949 | const char* argBestIdatTo = NULL; 1950 | const char* argOriginalIdatTo = NULL; 1951 | int argMaxTime = 0; 1952 | int argMaxStagnateTime = 5; 1953 | int argMaxEvaluations = 0; 1954 | int argMaxDeflate = 0; 1955 | int argPopulationSize = 19; 1956 | std::unique_ptr argOutDeflate; 1957 | std::unique_ptr argEstimator; 1958 | int argZlibLevel = 7; 1959 | std::vector argStripChunks; 1960 | bool argStripOptional = false; 1961 | std::vector argKeepChunks; 1962 | 1963 | #if !defined(_MSC_VER) && !defined(__MINGW32__) 1964 | sig_t old_handler; 1965 | #endif 1966 | 1967 | // Parse command line parameters 1968 | for (int ax = 1; ax < argc; ++ax) { 1969 | size_t nlen; 1970 | 1971 | const char* s = argv[ax]; 1972 | const char* value; 1973 | 1974 | // boolean options 1975 | if (strcmp("--help", s) == 0) { 1976 | show_help(); 1977 | return EXIT_SUCCESS; 1978 | 1979 | } else if (strcmp("--verbose-analysis", s) == 0) { 1980 | argVerboseAnalysis = true; 1981 | continue; 1982 | 1983 | } else if (strcmp("--verbose-summary", s) == 0) { 1984 | argVerboseSummary = true; 1985 | continue; 1986 | 1987 | } else if (strcmp("--verbose-genomes", s) == 0) { 1988 | argVerboseGenomes = true; 1989 | continue; 1990 | 1991 | } else if (strcmp("--exclude-original", s) == 0) { 1992 | argExcludeOriginal = true; 1993 | continue; 1994 | 1995 | } else if (strcmp("--exclude-singles", s) == 0) { 1996 | argExcludeSingles = true; 1997 | continue; 1998 | 1999 | } else if (strcmp("--exclude-heuristic", s) == 0) { 2000 | argExcludeHeuristic = true; 2001 | continue; 2002 | 2003 | } else if (strcmp("--verbose", s) == 0) { 2004 | argVerboseAnalysis = true; 2005 | argVerboseSummary = true; 2006 | argVerboseGenomes = true; 2007 | continue; 2008 | 2009 | } else if (strcmp("--info", s) == 0) { 2010 | argInfo = true; 2011 | argVerboseAnalysis = true; 2012 | continue; 2013 | 2014 | } else if (strcmp("--normalize-alpha", s) == 0) { 2015 | argNormalizeAlpha = true; 2016 | continue; 2017 | 2018 | } else if (strcmp("--even-if-bigger", s) == 0) { 2019 | argEvenIfBigger = true; 2020 | continue; 2021 | 2022 | } else if (strcmp("--bigger-is-better", s) == 0) { 2023 | argBiggerIsBetter = true; 2024 | continue; 2025 | 2026 | } else if (strcmp("--exclude-experiments", s) == 0) { 2027 | argExcludeExperiment1 = true; 2028 | argExcludeExperiment2 = true; 2029 | argExcludeExperiment3 = true; 2030 | argExcludeExperiment4 = true; 2031 | continue; 2032 | 2033 | } else if (strcmp("--strip-optional", s) == 0) { 2034 | argStripOptional = true; 2035 | continue; 2036 | 2037 | } else if (strcmp("--version", s) == 0) { 2038 | show_version(); 2039 | return EXIT_SUCCESS; 2040 | 2041 | } 2042 | 2043 | value = strchr(s, '='); 2044 | 2045 | if (value == NULL) { 2046 | fprintf(stderr, "pngwolf: option error '%s'", s); 2047 | return EXIT_FAILURE; 2048 | } 2049 | 2050 | nlen = value++ - s; 2051 | 2052 | // --name=value options 2053 | if (strncmp("--in", s, nlen) == 0) { 2054 | argPng = value; 2055 | 2056 | } else if (strncmp("--out", s, nlen) == 0) { 2057 | argOut = value; 2058 | 2059 | } else if (strncmp("--best-idat-to", s, nlen) == 0) { 2060 | argBestIdatTo = value; 2061 | 2062 | } else if (strncmp("--original-idat-to", s, nlen) == 0) { 2063 | argOriginalIdatTo = value; 2064 | 2065 | } else if (strncmp("--out-deflate", s, nlen) == 0) { 2066 | argOutDeflate = get_deflater_from_option(value); 2067 | if (argOutDeflate == nullptr) { 2068 | return EXIT_FAILURE; 2069 | } 2070 | 2071 | } else if (strncmp("--estimator", s, nlen) == 0) { 2072 | argEstimator = get_deflater_from_option(value); 2073 | if (argEstimator == nullptr) { 2074 | return EXIT_FAILURE; 2075 | } 2076 | 2077 | } else if (strncmp("--max-time", s, nlen) == 0) { 2078 | argMaxTime = atoi(value); 2079 | 2080 | } else if (strncmp("--max-stagnate-time", s, nlen) == 0) { 2081 | argMaxStagnateTime = atoi(value); 2082 | 2083 | } else if (strncmp("--max-deflate", s, nlen) == 0) { 2084 | argMaxDeflate = atoi(value); 2085 | 2086 | } else if (strncmp("--max-evaluations", s, nlen) == 0) { 2087 | argMaxEvaluations = atoi(value); 2088 | 2089 | } else if (strncmp("--population-size", s, nlen) == 0) { 2090 | argPopulationSize = atoi(value); 2091 | 2092 | } else if (strncmp("--zlib-level", s, nlen) == 0) { 2093 | argZlibLevel = atoi(value); 2094 | 2095 | if (argZlibLevel < 0 || argZlibLevel > 9) { 2096 | fprintf(stderr, "pngwolf: invalid zlib level %d\n", argZlibLevel); 2097 | return EXIT_FAILURE; 2098 | } 2099 | 2100 | } else if (strncmp("--strip-chunk", s, nlen) == 0) { 2101 | if (strlen(value) == 4) { 2102 | argStripChunks.push_back(ntohl(*(uint32_t *)value)); 2103 | } else { 2104 | fprintf(stderr, "pngwolf: invalid chunk name '%s'\n", value); 2105 | return EXIT_FAILURE; 2106 | } 2107 | 2108 | } else if (strncmp("--keep-chunk", s, nlen) == 0) { 2109 | if (strlen(value) == 4) { 2110 | argKeepChunks.push_back(ntohl(*(uint32_t *)value)); 2111 | } else { 2112 | fprintf(stderr, "pngwolf: invalid chunk name '%s'\n", value); 2113 | return EXIT_FAILURE; 2114 | } 2115 | 2116 | } else { 2117 | fprintf(stderr, "pngwolf: unknown option '%s'", s); 2118 | return EXIT_FAILURE; 2119 | } 2120 | } 2121 | 2122 | if (argPng == NULL) { 2123 | fprintf(stderr, "pngwolf: missing input file\n" 2124 | "Try `pngwolf --help` for more information.\n"); 2125 | return EXIT_FAILURE; 2126 | } 2127 | 2128 | if (argEstimator == nullptr) { 2129 | argEstimator = std::make_unique(); 2130 | } 2131 | 2132 | if (argOutDeflate == nullptr) { 2133 | argOutDeflate = std::make_unique(); 2134 | } 2135 | 2136 | wolf.deflate_estimator = std::move(argEstimator); 2137 | wolf.deflate_out = std::move(argOutDeflate); 2138 | wolf.zlib_level = argZlibLevel; 2139 | wolf.in_path = argPng; 2140 | wolf.max_deflate = argMaxDeflate; 2141 | wolf.max_evaluations = argMaxEvaluations; 2142 | wolf.verbose_analysis = argVerboseAnalysis; 2143 | wolf.verbose_genomes = argVerboseGenomes; 2144 | wolf.verbose_summary = argVerboseSummary; 2145 | wolf.exclude_heuristic = argExcludeHeuristic; 2146 | wolf.exclude_original = argExcludeOriginal; 2147 | wolf.exclude_singles = argExcludeSingles; 2148 | wolf.exclude_experiment1 = argExcludeExperiment1; 2149 | wolf.exclude_experiment2 = argExcludeExperiment2; 2150 | wolf.exclude_experiment3 = argExcludeExperiment3; 2151 | wolf.exclude_experiment4 = argExcludeExperiment4; 2152 | wolf.population_size = argPopulationSize; 2153 | wolf.max_stagnate_time = argMaxStagnateTime; 2154 | wolf.last_step_at = time(NULL); 2155 | wolf.last_improvement_at = time(NULL); 2156 | wolf.program_begun_at = time(NULL); 2157 | wolf.max_time = argMaxTime; 2158 | wolf.out_path = argOut; 2159 | wolf.normalize_alpha = argNormalizeAlpha; 2160 | wolf.even_if_bigger = argEvenIfBigger; 2161 | wolf.auto_mpass = argAutoMpass; 2162 | wolf.bigger_is_better = argBiggerIsBetter; 2163 | wolf.strip_chunks = argStripChunks; 2164 | wolf.strip_optional = argStripOptional; 2165 | wolf.keep_chunks = argKeepChunks; 2166 | 2167 | // TODO: ... 2168 | try { 2169 | if (wolf.read_file()) { 2170 | goto error; 2171 | } 2172 | } catch (...) { 2173 | goto error; 2174 | } 2175 | 2176 | if (argOriginalIdatTo != NULL) 2177 | if (wolf.save_original_idat(argOriginalIdatTo)) 2178 | goto out_error; 2179 | 2180 | wolf.init_filters(); 2181 | wolf.log_analysis(); 2182 | 2183 | if (argInfo) 2184 | goto done; 2185 | 2186 | wolf.search_begun_at = time(NULL); 2187 | 2188 | #if defined(_MSC_VER) || defined(__MINGW32__) 2189 | SetConsoleCtrlHandler(console_event_handler, TRUE); 2190 | #else 2191 | old_handler = signal(SIGINT, sigint_handler); 2192 | #endif 2193 | 2194 | wolf.run(); 2195 | 2196 | // Uninstall the SIGINT interceptor to allow users to abort 2197 | // the possibly very slow recompression step. This may lead 2198 | // to users unintentionally hit CTRL+C twice, but there is 2199 | // not much to avoid that, other than setting a timer with 2200 | // some grace perdiod which strikes me as too complicated. 2201 | 2202 | #if defined(_MSC_VER) || defined(__MINGW32__) 2203 | SetConsoleCtrlHandler(console_event_handler, FALSE); 2204 | #else 2205 | signal(SIGINT, old_handler); 2206 | #endif 2207 | 2208 | wolf.recompress(); 2209 | 2210 | if (wolf.out_path) 2211 | if (wolf.save_file()) 2212 | goto out_error; 2213 | 2214 | if (argBestIdatTo != NULL) 2215 | if (wolf.save_best_idat(argBestIdatTo)) 2216 | goto out_error; 2217 | 2218 | wolf.log_summary(); 2219 | 2220 | done: 2221 | return EXIT_SUCCESS; 2222 | 2223 | error: 2224 | if (wolf.ihdr.interlace) { 2225 | fprintf(stderr, "pngwolf: interlaced images are not supported\n"); 2226 | return EXIT_FAILURE; 2227 | } 2228 | 2229 | fprintf(stderr, "pngwolf: an error occured while reading the input file\n"); 2230 | return EXIT_FAILURE; 2231 | 2232 | out_error: 2233 | fprintf(stderr, "pngwolf: an error occured while writing an output file\n"); 2234 | return EXIT_FAILURE; 2235 | 2236 | } 2237 | 2238 | // TODO: There are probably integer overflow issues with really, 2239 | // really big image files. Really, really big image ones. Biiig. 2240 | --------------------------------------------------------------------------------