diff options
author | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:11:21 +0200 |
---|---|---|
committer | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:51:35 +0200 |
commit | ae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch) | |
tree | 5f462459ae4b47d22114eed717d1382d08cf4dfe /image/png |
Diffstat (limited to 'image/png')
-rw-r--r-- | image/png/COPYING | 367 | ||||
-rw-r--r-- | image/png/README | 8 | ||||
-rw-r--r-- | image/png/decoder.ha | 171 | ||||
-rw-r--r-- | image/png/errors.ha | 35 | ||||
-rw-r--r-- | image/png/filter.ha | 75 | ||||
-rw-r--r-- | image/png/idat.ha | 82 | ||||
-rw-r--r-- | image/png/iend.ha | 27 | ||||
-rw-r--r-- | image/png/ihdr.ha | 114 | ||||
-rw-r--r-- | image/png/load.ha | 88 | ||||
-rw-r--r-- | image/png/paeth.ha | 30 | ||||
-rw-r--r-- | image/png/plte.ha | 95 | ||||
-rw-r--r-- | image/png/reader.ha | 175 | ||||
-rw-r--r-- | image/png/test_data.ha | 638 | ||||
-rw-r--r-- | image/png/types.ha | 69 |
14 files changed, 1974 insertions, 0 deletions
diff --git a/image/png/COPYING b/image/png/COPYING new file mode 100644 index 0000000..c257317 --- /dev/null +++ b/image/png/COPYING @@ -0,0 +1,367 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. diff --git a/image/png/README b/image/png/README new file mode 100644 index 0000000..c39aaa4 --- /dev/null +++ b/image/png/README @@ -0,0 +1,8 @@ +This module provides support for decoding PNG images per the PNG specification +version 1.2: + +http://www.libpng.org/pub/png/spec/1.2/ + +To read an image all at once, see [[load]]. The PNG image data is returned +unmodified; the user is responsible for converting to the pixel format of their +choice. To read an image incrementally, see [[newreader]]. diff --git a/image/png/decoder.ha b/image/png/decoder.ha new file mode 100644 index 0000000..4516bb7 --- /dev/null +++ b/image/png/decoder.ha @@ -0,0 +1,171 @@ +use bufio; +use bytes; +use compress::zlib; +use io; + +export type decoder = struct { + vtable: io::stream, + src: io::handle, + inflate: zlib::reader, + ihdr: *ihdr, + filter: (filter | void), + bpp: size, // bytes per pixel, rounded up + buffered: size, + cr: []u8, + pr: []u8, +}; + +// Returns a new image decoder, from which raw pixel data may be read via +// [[io::read]]. The input should be a compressed IDAT data stream, normally +// obtained via an [[idat_reader]]. +// +// The user must provide a pixel buffer of suitable size to store two scanlines +// for image filtering, or larger for interlaced images; see [[decoder_bufsiz]] +// to determine the appropriate length. +// +// The header is borrowed from the input and should remain valid until the +// decoder is no longer in use. +// +// The user must call [[io::close]] afterwards to free resources allocated for +// the decompressor. The call never returns an error. +export fn newdecoder( + src: io::handle, + ihdr: *ihdr, + buf: []u8, +) (decoder | error) = { + assert(len(buf) == decoder_bufsiz(ihdr), + "Incorrect buffer length provided for PNG decoder"); + bytes::zero(buf); + return decoder { + vtable = &decoder_vtable, + src = src, + inflate = zlib::decompress(src)?, + ihdr = ihdr, + filter = void, + bpp = bytesperpixel(ihdr), + buffered = 0, + cr = buf[..len(buf) / 2], + pr = buf[len(buf) / 2..], + }; +}; + +const decoder_vtable: io::vtable = io::vtable { + reader = &decoder_read, + closer = &decoder_close, + ... +}; + +fn decoder_read(st: *io::stream, buf: []u8) (size | io::EOF | io::error) = { + let dec = st: *decoder; + assert(dec.vtable == &decoder_vtable); + + if (dec.buffered != 0) { + return decoder_copy(dec, buf); + }; + + if (dec.filter is void) { + const ft = match (bufio::read_byte(&dec.inflate)?) { + case io::EOF => + if (!(io::read(dec.src, &[0u8])? is io::EOF)) { + // Extra data following zlib stream + return wraperror(invalid); + }; + return io::EOF; + case let b: u8 => + yield b: filter; + }; + if (ft > filter::PAETH) { + return wraperror(unsupported); + }; + dec.filter = ft; + }; + + if (dec.ihdr.interlace == interlace::ADAM7) { + abort(); // TODO + }; + + // Read one scanline + match (io::readall(&dec.inflate, dec.cr)) { + case io::EOF => + return wraperror(invalid); + case let err: io::error => + if (err is io::underread) { + return wraperror(invalid); + }; + return err; + case size => void; + }; + + applyfilter(dec); + dec.buffered = len(dec.cr); + return decoder_copy(dec, buf); +}; + +fn decoder_close(st: *io::stream) (void | io::error) = { + let dec = st: *decoder; + assert(dec.vtable == &decoder_vtable); + io::close(&dec.inflate)?; +}; + +// Returns the size of a buffer suitable to store decoder state for this image. +// The computed size is twice the length of a scanline in bytes, unless the +// image is interlaced, in which case the buffer size is the full length +// necessary to store the complete image. +export fn decoder_bufsiz(ihdr: *ihdr) size = { + let sampledepth = ihdr.bitdepth: uint; + const samples: uint = switch (ihdr.colortype) { + case colortype::GRAYSCALE => + yield 1; + case colortype::RGB => + yield 3; + case colortype::PLTE => + yield 1; + case colortype::GRAYALPHA => + yield 2; + case colortype::RGBA => + yield 4; + }; + const width = ihdr.width: uint, height = ihdr.height: uint; + + let scanline = width * samples * sampledepth; // in bits + if (scanline % 8 != 0) { + // Pad to nearest byte + scanline += 8 - (scanline % 8); + }; + + if (ihdr.interlace == interlace::ADAM7) { + return scanline * height; + }; + + return (scanline / 8) * 2; // Two scanlines +}; + +fn bytesperpixel(ihdr: *ihdr) size = { + let sampledepth = ihdr.bitdepth: uint; + const samples: uint = switch (ihdr.colortype) { + case colortype::GRAYSCALE => + yield 1; + case colortype::RGB => + yield 3; + case colortype::PLTE => + yield 1; + case colortype::GRAYALPHA => + yield 2; + case colortype::RGBA => + yield 4; + }; + return (sampledepth * samples + 7) / 8; +}; + +// Copies pending pixel data into a user buffer. +fn decoder_copy(dec: *decoder, buf: []u8) size = { + let max = dec.buffered; + if (len(buf) < max) { + max = len(buf); + }; + assert(max > 0); + buf[..max] = dec.cr[..max]; + dec.cr[..len(dec.cr)-max] = dec.cr[max..]; + dec.buffered -= max; + return max; +}; diff --git a/image/png/errors.ha b/image/png/errors.ha new file mode 100644 index 0000000..8f41ddb --- /dev/null +++ b/image/png/errors.ha @@ -0,0 +1,35 @@ +use errors; +use io; + +// Invalid PNG data was encountered. +export type invalid = !void; + +// PNG data using unsupported features was encountered. +export type unsupported = !void; + +// All errors which could be returned from this module. +export type error = !(io::error | invalid | unsupported); + +// Returns a human-friendly error string. +export fn strerror(err: error) const str = { + match (err) { + case let err: io::error => + return io::strerror(err); + case invalid => + return "Invalid PNG format"; + case unsupported => + return "Provided PNG file requires unsupported features"; + }; +}; + +fn wraperror(err: (invalid | unsupported)) errors::opaque_ = { + static assert(size((invalid | unsupported)) <= size(errors::opaque_data)); + let wrapped = errors::opaque_ { strerror = &opaque_strerror, ... }; + let ptr = &wrapped.data: *error; + *ptr = err; + return wrapped; +}; + +fn opaque_strerror(err: *errors::opaque_data) const str = { + return strerror(*(err: *(invalid | unsupported))); +}; diff --git a/image/png/filter.ha b/image/png/filter.ha new file mode 100644 index 0000000..5ae54e9 --- /dev/null +++ b/image/png/filter.ha @@ -0,0 +1,75 @@ +use bytes; +use io; +use memio; + +// Filter modes supported by PNG files. +export type filter = enum u8 { + NONE = 0, + SUB = 1, + UP = 2, + AVERAGE = 3, + PAETH = 4, +}; + +fn applyfilter(dec: *decoder) void = { + const mode = dec.filter as filter; + const bpp = dec.bpp; + switch (mode) { + case filter::NONE => + yield; // no-op + case filter::SUB => + for (let i = bpp; i < len(dec.cr); i += 1) { + dec.cr[i] += dec.cr[i - bpp]; + }; + case filter::UP => + for (let i = 0z; i < len(dec.cr); i += 1) { + dec.cr[i] += dec.pr[i]; + }; + case filter::AVERAGE => + for (let i = 0z; i < bpp; i += 1) { + dec.cr[i] += dec.pr[i] / 2; + }; + for (let i = bpp; i < len(dec.cr); i += 1) { + dec.cr[i] += ((dec.cr[i - bpp]: int + dec.pr[i]: int) / 2): u8; + }; + case filter::PAETH => + applypaeth(dec); + }; + dec.filter = void; + dec.pr[..] = dec.cr[..]; +}; + +@test fn filter_none() void = { + const src = memio::fixed(no_filtering); + const image = load(&src)!; + defer image_finish(&image); + assert(bytes::equal(image.pixels, no_filtering_data)); +}; + +@test fn filter_sub() void = { + const src = memio::fixed(filter_sub_png); + const image = load(&src)!; + defer image_finish(&image); + assert(bytes::equal(image.pixels, filter_sub_data)); +}; + +@test fn filter_up() void = { + const src = memio::fixed(filter_up_png); + const image = load(&src)!; + defer image_finish(&image); + assert(bytes::equal(image.pixels, filter_up_data)); +}; + +@test fn filter_average() void = { + const src = memio::fixed(filter_avg_png); + const image = load(&src)!; + defer image_finish(&image); + assert(bytes::equal(image.pixels, filter_avg_data)); +}; + +@test fn filter_peath() void = { + const src = memio::fixed(filter_peath_png); + const image = load(&src)!; + defer image_finish(&image); + assert(bytes::equal(image.pixels, filter_peath_data)); +}; diff --git a/image/png/idat.ha b/image/png/idat.ha new file mode 100644 index 0000000..dd2e749 --- /dev/null +++ b/image/png/idat.ha @@ -0,0 +1,82 @@ +use bufio; +use bytes; +use io; +use memio; + +export type idat_reader = struct { + vtable: io::stream, + src: *reader, +}; + +// Returns a new IDAT reader, which reads consecutive IDAT chunks from a +// [[reader]] and outputs their raw (compressed) contents as a continuous data +// stream. Instead of reading from this directly, users are advised to construct +// a [[decoder]] with [[newdecoder]], and use that to read the uncompressed +// pixel data. +// +// This stream does not need to be closed. +export fn new_idat_reader(src: *reader) idat_reader = { + assert(chunk_type(src) == IDAT, + "Attempted to create IDAT reader for non-IDAT chunk"); + return idat_reader { + vtable = &idat_reader_vtable, + src = src, + }; +}; + +const idat_reader_vtable: io::vtable = io::vtable { + reader = &idat_read, + ... +}; + +fn idat_read( + st: *io::stream, + buf: []u8, +) (size | io::EOF | io::error) = { + let st = st: *idat_reader; + + assert(st.vtable == &idat_reader_vtable); + + for (true) match (io::read(st.src, buf)?) { + case io::EOF => + match (nextchunk(st.src)) { + case let err: (invalid | unsupported) => + return wraperror(err); + case let err: io::error => + return err; + case io::EOF => + return io::EOF; + case let ctype: u32 => + if (ctype != IDAT) { + return io::EOF; + }; + }; + case let z: size => + return z; + }; +}; + +@test fn idat_reader() void = { + const src = memio::fixed(no_filtering); + const read = newreader(&src) as reader; + assert(nextchunk(&read) as u32 == IHDR); + const ihdr = ihdr_read(&read)!; + + let pixbuf: []u8 = alloc([0...], decoder_bufsiz(&ihdr)); + defer free(pixbuf); + + for (true) { + const ctype = nextchunk(&read) as u32; + if (ctype == IDAT) { + break; + }; + io::copy(io::empty, &read)!; + }; + + let idat = new_idat_reader(&read); + let dec = newdecoder(&idat, &ihdr, pixbuf)!; + defer io::close(&dec)!; + const pixels = io::drain(&dec)!; + defer free(pixels); + assert(bytes::equal(pixels, no_filtering_data)); +}; diff --git a/image/png/iend.ha b/image/png/iend.ha new file mode 100644 index 0000000..97651a3 --- /dev/null +++ b/image/png/iend.ha @@ -0,0 +1,27 @@ +use bufio; +use io; +use memio; + +// Reads an IEND chunk from a [[reader]]. This function simply performs a sanity +// check and verifies the chunk checksum. +export fn iend_read(src: *reader) (void | error) = { + assert(chunk_type(src) == IEND, + "Attempted to call iend_read on non-IEND chunk"); + io::copy(io::empty, src)?; +}; + +@test fn iend_reader() void = { + const src = memio::fixed(simple_png); + const read = newreader(&src) as reader; + assert(nextchunk(&read) as u32 == IHDR); + io::copy(io::empty, &read)!; + + for (true) { + if (nextchunk(&read) as u32 == IEND) { + break; + }; + io::copy(io::empty, &read)!; + }; + + iend_read(&read)!; +}; diff --git a/image/png/ihdr.ha b/image/png/ihdr.ha new file mode 100644 index 0000000..bbb8e3e --- /dev/null +++ b/image/png/ihdr.ha @@ -0,0 +1,114 @@ +use endian; +use io; +use memio; + +// A PNG IHDR chunk. +export type ihdr = struct { + width: u32, + height: u32, + bitdepth: u8, + colortype: colortype, + compression: compression, + filter_mode: filter_mode, + interlace: interlace, +}; + +// Pixel formats supported by PNG files. +export type colortype = enum u8 { + GRAYSCALE = 0, + RGB = 2, + PLTE = 3, + GRAYALPHA = 4, + RGBA = 6, +}; + +// Compression types supported by PNG files. +export type compression = enum u8 { + DEFLATE = 0, +}; + +export type filter_mode = enum u8 { + ADAPTIVE = 0, +}; + +// Interlace types supported by PNG files. +export type interlace = enum u8 { + NONE = 0, + ADAM7 = 1, +}; + +// Reads an [[ihdr]] from a [[reader]]. +export fn ihdr_read(src: *reader) (ihdr | error) = { + assert(chunk_type(src) == IHDR, + "Attempted to call ihdr_read on non-IHDR chunk"); + let buf: [13]u8 = [0...]; + match (io::readall(src, buf)?) { + case io::EOF => + return invalid; + case size => + yield; + }; + // Read checksum + if (!(io::read(src, &[0u8])? is io::EOF)) { + return invalid; + }; + const hdr = ihdr { + width = endian::begetu32(buf[..4]), + height = endian::begetu32(buf[4..8]), + bitdepth = buf[8], + colortype = buf[9]: colortype, + compression = buf[10]: compression, + filter_mode = buf[11]: filter_mode, + interlace = buf[12]: interlace, + }; + if ((hdr.width | hdr.height) & 0x80000000 > 0) { + return invalid; + }; + switch (hdr.colortype) { + case colortype::GRAYSCALE => + if (hdr.bitdepth != 1 && hdr.bitdepth != 2 && hdr.bitdepth != 4 + && hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case colortype::RGB => + if (hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case colortype::PLTE => + if (hdr.bitdepth != 1 && hdr.bitdepth != 2 + && hdr.bitdepth != 4 && hdr.bitdepth != 8) { + return invalid; + }; + case colortype::GRAYALPHA => + if (hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case colortype::RGBA => + if (hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case => + return invalid; + }; + if (hdr.compression != compression::DEFLATE) { + return unsupported; + }; + if (hdr.filter_mode != filter_mode::ADAPTIVE) { + return unsupported; + }; + if (hdr.interlace > interlace::ADAM7) { + return unsupported; + }; + return hdr; +}; + +@test fn ihdr_reader() void = { + const src = memio::fixed(simple_png); + const read = newreader(&src) as reader; + assert(nextchunk(&read) as u32 == IHDR); + const ihdr = ihdr_read(&read)!; + assert(ihdr.width == 4 && ihdr.height == 4); + assert(ihdr.colortype == colortype::RGB); + assert(ihdr.filter_mode == filter_mode::ADAPTIVE); + assert(ihdr.interlace == interlace::NONE); +}; diff --git a/image/png/load.ha b/image/png/load.ha new file mode 100644 index 0000000..d3578f8 --- /dev/null +++ b/image/png/load.ha @@ -0,0 +1,88 @@ +use errors; +use io; +use memio; + +// A decoded PNG image. +export type image = struct { + // Image header + ihdr: ihdr, + // RGBA palette data (if present, otherwise an empty slice) + palette: []u32, + // Raw pixel data + pixels: []u8, +}; + +// Reads a PNG image from an [[io::handle]], returning the decoded header, +// palette (if present), and raw pixel data. The caller must pass the return +// value to [[image_finish]] to discard resources associated with the image. +export fn load(in: io::handle) (image | error) = { + let success = false; + + const reader = newreader(in)?; + + match (nextchunk(&reader)?) { + case io::EOF => + return invalid; + case let ctype: u32 => + if (ctype != IHDR) { + return invalid; + }; + }; + const ihdr = ihdr_read(&reader)?; + let pixels: ([]u8 | void) = void; + defer if (!success && pixels is []u8) free(pixels as []u8); + let palette: []u32 = []; + defer if (!success) free(palette); + + for (true) match (nextchunk(&reader)?) { + case io::EOF => + return invalid; + case let ctype: u32 => + switch (ctype) { + case IHDR => + return invalid; + case PLTE => + if (len(palette) != 0) { + return invalid; + }; + let plte = new_plte_reader(&reader)?; + palette = alloc([0...], plte_ncolors(&plte)); + plte_read(&plte, palette)?; + case IDAT => + if (pixels is []u8) { + return invalid; + }; + let pixbuf: []u8 = alloc([0...], decoder_bufsiz(&ihdr)); + defer free(pixbuf); + let idat = new_idat_reader(&reader); + let dec = newdecoder(&idat, &ihdr, pixbuf)?; + defer io::close(&dec)!; + pixels = io::drain(&dec)?; + case IEND => + iend_read(&reader)?; + break; + case => + if (is_critical(ctype)) { + return unsupported; + }; + io::copy(io::empty, &reader)?; + }; + }; + + if (pixels is void) { + return invalid; + }; + + success = true; + return image { + ihdr = ihdr, + palette = palette, + pixels = pixels as []u8, + }; +}; + +// Frees resources associated with an [[image]]. +export fn image_finish(img: *image) void = { + free(img.palette); + free(img.pixels); +}; diff --git a/image/png/paeth.ha b/image/png/paeth.ha new file mode 100644 index 0000000..4abc373 --- /dev/null +++ b/image/png/paeth.ha @@ -0,0 +1,30 @@ +// Based on Go's implementation; BSD license +use math; + +fn applypaeth(dec: *decoder) void = { + const bpp = dec.bpp; + let a = 0i32, b = 0i32, c = 0i32, pa = 0i32, pb = 0i32, pc = 0i32; + for (let i = 0z; i < bpp; i += 1) { + a = 0; + c = 0; + for (let j = i; j < len(dec.cr); j += bpp) { + b = dec.pr[j]: i32; + pa = b - c; + pb = a - c; + pc = math::absi32(pa + pb): i32; + pa = math::absi32(pa): i32; + pb = math::absi32(pb): i32; + if (pa <= pb && pa <= pc) { + void; // no-op + } else if (pb <= pc) { + a = b; + } else { + a = c; + }; + a += dec.cr[j]: i32; + a &= 0xff; + dec.cr[j] = a: u8; + c = b; + }; + }; +}; diff --git a/image/png/plte.ha b/image/png/plte.ha new file mode 100644 index 0000000..a19a263 --- /dev/null +++ b/image/png/plte.ha @@ -0,0 +1,95 @@ +use io; +use memio; + +export type plte_reader = struct { + src: *reader, +}; + +// Returns a new PLTE reader for a [[chunk_reader]]. +export fn new_plte_reader(src: *reader) (plte_reader | invalid) = { + assert(chunk_type(src) == PLTE, + "Attempted to create PLTE reader for non-PLTE chunk"); + const length = chunk_length(src); + if (length % 3 != 0) { + return invalid; + }; + const ncolor = length / 3; + if (ncolor < 1 && ncolor > 256) { + return invalid; + }; + return plte_reader { + src = src, + }; +}; + +// Returns the number of colors defined by this palette. The return value will +// be between 1 and 256 (inclusive), so should the caller wish to statically +// allocate a suitable buffer, [256]u32 will suffice for all cases. +export fn plte_ncolors(pr: *plte_reader) size = { + return chunk_length(pr.src) / 3; +}; + +// Reads the palette from a PLTE chunk. The caller must provide a buffer equal +// in length to the return value of [[plte_ncolors]], which will be filled with +// RGB colors, such that the most significant byte is red and the least +// significant byte is unused (set to 0xFF). Returns the number of colors in the +// palette. +export fn plte_read(pr: *plte_reader, buf: []u32) (size | error) = { + const ncolor = plte_ncolors(pr); + assert(len(buf) == ncolor, "Invalid buffer size for plte_read"); + + static let rbuf: [3 * 256]u8 = [0...]; + for (let i = 0z; i < ncolor) { + let n = (ncolor - i) * 3; + if (n > len(rbuf)) { + n = len(rbuf); + }; + match (io::readall(pr.src, rbuf[..n])?) { + case io::EOF => + return invalid; + case size => + yield; + }; + for (let q = 0z; q < n / 3; q += 1) { + const r = rbuf[q * 3 + 0]: u32; + const g = rbuf[q * 3 + 1]: u32; + const b = rbuf[q * 3 + 2]: u32; + buf[i + q] = (r << 24) | (g << 16) | (b << 8) | 0xFF; + }; + i += n / 3; + }; + // required so that the checksum gets read + assert(io::read(pr.src, rbuf[..1])? is io::EOF); + return ncolor; +}; + +@test fn plte_reader() void = { + const src = memio::fixed(palette_sample); + const read = newreader(&src) as reader; + assert(nextchunk(&read) as u32 == IHDR); + io::copy(io::empty, &read)!; + + for (true) { + const ctype = nextchunk(&read) as u32; + if (ctype == PLTE) { + break; + }; + io::copy(io::empty, &read)!; + }; + + const plte = new_plte_reader(&read)!; + + let buf: []u32 = alloc([0...], plte_ncolors(&plte)); + defer free(buf); + plte_read(&plte, buf)!; + + const expected: [_]u32 = [ + 0x2200FFFF, 0x00FFFFFF, 0x8800FFFF, 0x22FF00FF, 0x0099FFFF, + 0xFF6600FF, 0xDD00FFFF, 0x77FF00FF, 0xFF0000FF, 0x00FF99FF, + 0xDDFF00FF, 0xFF00BBFF, 0xFFBB00FF, 0x0044FFFF, 0x00FF44FF, + ]; + assert(len(expected) == len(buf)); + for (let i = 0z; i < len(expected); i += 1) { + assert(buf[i] == expected[i]); + }; +}; diff --git a/image/png/reader.ha b/image/png/reader.ha new file mode 100644 index 0000000..148a11f --- /dev/null +++ b/image/png/reader.ha @@ -0,0 +1,175 @@ +use bytes; +use endian; +use hash::crc32; +use hash; +use io; +use memio; + +export type reader = struct { + vtable: io::stream, + src: io::handle, + status: reader_status, + length: size, + ctype: u32, + crc: crc32::state, +}; + +export type reader_status = enum u8 { + // cursor is at the start of a chunk header (or at EOF) + NEXT, + // cursor is at the end of a chunk header + HEADER_READ, + // cursor is within chunk data + READING, +}; + +// Creates a new PNG decoder. Reads and verifies the PNG magic before returning +// the reader object. Call [[nextchunk]] to read the next chunk from the input. +export fn newreader(src: io::handle) (reader | error) = { + let buf: [MAGIC_LEN]u8 = [0...]; + match (io::readall(src, buf[..])?) { + case io::EOF => + return invalid; + case size => + yield; + }; + if (!bytes::equal(buf, magic)) { + return invalid; + }; + return reader { + vtable = &reader_vtable, + src = src, + status = reader_status::NEXT, + length = 0, + ctype = 0, + crc = crc32::crc32(&crc32::ieee_table), + }; +}; + +@test fn newreader() void = { + const src = memio::fixed([]); + assert(newreader(&src) as error is invalid); + + const src = memio::fixed(magic); + assert(newreader(&src) is reader); +}; + +// Starts decoding a new chunk from the reader, returning its type. The contents +// of the chunk can be read by calling [[io::read]] on the reader until it +// returns [[io::EOF]]. +// +// However, in an ideal scenario, the caller will not read directly from the +// chunk, but instead will select a chunk-type-aware decoder based on the +// returned chunk type, and use that to read the chunk's contents. +// +// Calling this function repeatedly will return the current chunk type with no +// side effects until [[io::read]] is called on the reader, after which the +// user must read the contents of the chunk until [[io::EOF]] is returned before +// calling nextchunk again. If the checksum fails verification, [[invalid]] is +// returned from [[io::read]]. +export fn nextchunk(src: *reader) (u32 | io::EOF | error) = { + assert(src.status != reader_status::READING, + "Must finish previous chunk before calling nextchunk again"); + + if (src.status == reader_status::NEXT) { + let buf: [8]u8 = [0...]; + match (io::readall(src.src, buf[..])?) { + case io::EOF => + return io::EOF; + case size => + yield; + }; + src.status = reader_status::HEADER_READ; + src.length = endian::begetu32(buf[..4]); + src.ctype = endian::begetu32(buf[4..]); + src.crc = crc32::crc32(&crc32::ieee_table); + hash::write(&src.crc, buf[4..]); + }; + + return src.ctype; +}; + +const reader_vtable: io::vtable = io::vtable { + reader = &read, + ... +}; + +// Returns the type of the chunk being read. +export fn chunk_type(src: *reader) u32 = { + assert(src.status != reader_status::NEXT, + "Must call nextchunk before calling chunk_type"); + return src.ctype; +}; + +// Returns the remaining length of the chunk being read in bytes, not including +// the header or checksum (that is, it returns the length of the chunk data). +export fn chunk_length(src: *reader) size = { + assert(src.status != reader_status::NEXT, + "Must call nextchunk before calling chunk_length"); + return src.length; +}; + +fn read(st: *io::stream, buf: []u8) (size | io::EOF | io::error) = { + let st = st: *reader; + assert(st.vtable == &reader_vtable); + + if (st.status == reader_status::NEXT) { + return io::EOF; + }; + st.status = reader_status::READING; + + if (st.length == 0) { + let ckbuf: [4]u8 = [0...]; + match (io::readall(st.src, ckbuf[..])?) { + case io::EOF => + return wraperror(invalid); + case size => + yield; + }; + st.status = reader_status::NEXT; + const want = endian::begetu32(ckbuf); + const have = crc32::sum32(&st.crc); + if (want != have) { + return wraperror(invalid); + }; + return io::EOF; + }; + + const max = if (len(buf) < st.length) { + yield len(buf); + } else { + yield st.length; + }; + + const z = match (io::read(st.src, buf[..max])?) { + case io::EOF => + // Missing checksum + return wraperror(invalid); + case let z: size => + yield z; + }; + + hash::write(&st.crc, buf[..z]); + st.length -= z; + return z; +}; + +@test fn nextchunk() void = { + const src = memio::fixed(magic); + const read = newreader(&src) as reader; + assert(nextchunk(&read) is io::EOF); + + const src = memio::fixed(simple_png); + const read = newreader(&src) as reader; + assert(nextchunk(&read) as u32 == IHDR); + let buf: [32]u8 = [0...]; + assert(io::read(&read, buf) as size == 13); + assert(io::read(&read, buf) is io::EOF); + assert(bytes::equal(buf[..13], simple_png[16..16+13])); + + const src = memio::fixed(invalid_chunk); + const read = newreader(&src) as reader; + nextchunk(&read) as u32; + assert(io::read(&read, buf) as size == 13); + assert(io::read(&read, buf) is io::error); +}; diff --git a/image/png/test_data.ha b/image/png/test_data.ha new file mode 100644 index 0000000..3e7a616 --- /dev/null +++ b/image/png/test_data.ha @@ -0,0 +1,638 @@ +const simple_png: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x26, 0x93, 0x09, 0x29, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x2e, 0x23, 0x00, 0x00, 0x2e, + 0x23, 0x01, 0x78, 0xa5, 0x3f, 0x76, 0x00, 0x00, 0x00, 0x1e, 0x49, 0x44, + 0x41, 0x54, 0x08, 0xd7, 0x3d, 0xc6, 0xa1, 0x01, 0x00, 0x00, 0x08, 0x03, + 0x20, 0xe6, 0xff, 0x3f, 0xcf, 0xa4, 0x24, 0x52, 0x90, 0xc2, 0xfc, 0x10, + 0x37, 0xe9, 0xfc, 0xb0, 0xab, 0xa3, 0x06, 0x05, 0x5f, 0xaa, 0xb1, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +]; + +const invalid_chunk: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x08, 0x02, 0x00, 0x01, 0x00, 0x26, 0x93, 0x09, 0x29, +]; + +const palette_sample: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x04, 0x03, 0x00, 0x00, 0x00, 0x81, 0x54, 0x67, 0xc7, 0x00, 0x00, 0x00, + 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x01, 0x86, 0xa0, 0x31, 0xe8, 0x96, + 0x5f, 0x00, 0x00, 0x00, 0x03, 0x73, 0x42, 0x49, 0x54, 0x04, 0x04, 0x04, + 0x77, 0xf8, 0xb5, 0xa3, 0x00, 0x00, 0x00, 0x2d, 0x50, 0x4c, 0x54, 0x45, + 0x22, 0x00, 0xff, 0x00, 0xff, 0xff, 0x88, 0x00, 0xff, 0x22, 0xff, 0x00, + 0x00, 0x99, 0xff, 0xff, 0x66, 0x00, 0xdd, 0x00, 0xff, 0x77, 0xff, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x99, 0xdd, 0xff, 0x00, 0xff, 0x00, 0xbb, + 0xff, 0xbb, 0x00, 0x00, 0x44, 0xff, 0x00, 0xff, 0x44, 0xd2, 0xb0, 0x49, + 0xbd, 0x00, 0x00, 0x00, 0x47, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, + 0xe8, 0xe8, 0x08, 0x0d, 0x3d, 0x73, 0x66, 0xd5, 0xaa, 0xf2, 0x72, 0x63, + 0xe3, 0x77, 0xef, 0x66, 0xce, 0x64, 0x20, 0x43, 0x00, 0x95, 0x2b, 0x28, + 0x48, 0x8e, 0x00, 0x2a, 0xd7, 0xc5, 0x85, 0x1c, 0x01, 0x54, 0xee, 0xdd, + 0xbb, 0xe4, 0x08, 0xa0, 0x71, 0x19, 0xc8, 0x11, 0x40, 0xe5, 0x2a, 0x29, + 0x91, 0x23, 0x80, 0xca, 0x4d, 0x4b, 0x23, 0x47, 0x00, 0x95, 0xbb, 0x7b, + 0x37, 0x19, 0x02, 0x00, 0xe0, 0xc4, 0xea, 0xd1, 0x0f, 0x82, 0x05, 0x7d, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +]; + +const no_filtering: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x56, 0x11, 0x25, 0x28, 0x00, 0x00, 0x01, + 0x06, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x85, 0xd2, 0x2f, 0x4c, 0x42, + 0x51, 0x14, 0xc7, 0xf1, 0xaf, 0xe0, 0x18, 0x73, 0x63, 0x06, 0x37, 0x37, + 0x02, 0x81, 0x62, 0xa0, 0x58, 0x48, 0xaf, 0xbc, 0xf4, 0x9a, 0x89, 0x66, + 0xb3, 0x11, 0x69, 0x54, 0x23, 0x8d, 0x48, 0xb3, 0xd1, 0x6c, 0x26, 0x12, + 0x89, 0x62, 0xb1, 0x58, 0x0c, 0x16, 0x37, 0x83, 0x9b, 0x1b, 0x9b, 0x9b, + 0x73, 0x0c, 0xe4, 0xe9, 0xfd, 0xe3, 0x76, 0xce, 0xf5, 0x85, 0x73, 0xeb, + 0xfd, 0xec, 0x9e, 0xdf, 0xfd, 0xdd, 0xcb, 0xf5, 0x64, 0x3a, 0xbb, 0x99, + 0xdf, 0xde, 0x2d, 0x96, 0xab, 0xfb, 0x87, 0xc7, 0xa7, 0xe7, 0x97, 0xd7, + 0xb7, 0xf7, 0xf5, 0xc7, 0xe7, 0xd7, 0x66, 0xbb, 0xfb, 0xde, 0x97, 0xe5, + 0x0f, 0x63, 0x43, 0x30, 0x32, 0x04, 0x43, 0x43, 0x70, 0x65, 0x08, 0x2e, + 0x0d, 0xc1, 0xc0, 0x10, 0x5c, 0x18, 0x82, 0x42, 0x0b, 0xc2, 0x4a, 0x04, + 0xb9, 0x12, 0x50, 0x15, 0x64, 0x22, 0xdc, 0x96, 0x9f, 0x92, 0x0a, 0xfa, + 0x22, 0x20, 0xe6, 0x00, 0x95, 0x83, 0x73, 0x11, 0x10, 0x92, 0xba, 0x13, + 0x54, 0x52, 0x7a, 0x22, 0xc0, 0x27, 0xf5, 0x21, 0xd4, 0x5d, 0x38, 0x13, + 0x81, 0x7f, 0xb9, 0x90, 0x52, 0xdd, 0x96, 0xae, 0x08, 0xf0, 0x39, 0x70, + 0x67, 0xa8, 0x3e, 0xe8, 0x88, 0x00, 0x37, 0x05, 0x7f, 0x5b, 0xd5, 0x18, + 0x6d, 0x11, 0xe1, 0xf4, 0x30, 0x45, 0x75, 0xca, 0xa9, 0x08, 0xbf, 0x1f, + 0xfb, 0x50, 0xad, 0x73, 0x22, 0xc2, 0xed, 0x27, 0x7d, 0x04, 0xc1, 0xb1, + 0x08, 0x48, 0xfa, 0x88, 0x82, 0x96, 0x08, 0xa2, 0x20, 0xf6, 0xf1, 0x27, + 0x38, 0x4a, 0x45, 0x58, 0xfa, 0xf5, 0x69, 0x56, 0x45, 0xf2, 0x3f, 0x68, + 0x24, 0x22, 0xcb, 0x8b, 0x7f, 0x3f, 0x88, 0x43, 0x43, 0x50, 0x37, 0x04, + 0x35, 0x43, 0x70, 0x60, 0x08, 0x2c, 0x81, 0x25, 0xb0, 0xc4, 0x2f, 0xc4, + 0x7d, 0xca, 0x58, 0x50, 0xa6, 0xce, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +]; + +const no_filtering_data: [_]u8 = [ + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xfe, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x00, 0x00, 0x00, 0x9d, 0xa4, + 0xab, 0xb2, 0x00, 0x00, 0x00, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x00, 0x00, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0x00, + 0x00, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, + 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0x00, 0x00, 0x00, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x00, 0x00, 0x7f, 0x87, 0x8e, + 0x96, 0x00, 0x00, 0x00, 0x00, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x00, 0x00, 0x76, 0x7f, 0x87, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x00, + 0x00, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x00, 0x00, 0x67, 0x00, 0x00, + 0x00, 0x87, 0x8e, 0x00, 0x00, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x7f, 0x87, 0x00, + 0x00, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x00, + 0x00, 0x00, 0x00, 0x67, 0x6f, 0x76, 0x7f, 0x00, 0x00, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x00, 0x00, 0x00, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x00, 0x00, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x00, 0x00, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, + 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x00, + 0x00, 0x00, 0x4c, 0x52, 0x59, 0x60, 0x00, 0x00, 0x00, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x01, 0x02, 0x03, 0x04, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, +]; + +const filter_sub_png: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x56, 0x11, 0x25, 0x28, 0x00, 0x00, 0x01, + 0x08, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x85, 0xd2, 0x21, 0x4b, 0xc4, + 0x41, 0x10, 0x05, 0xf0, 0xf7, 0x76, 0x76, 0x76, 0xf6, 0x04, 0x31, 0x08, + 0x82, 0xc1, 0x60, 0x31, 0x58, 0x2c, 0x26, 0x8b, 0xc9, 0x66, 0x12, 0x0c, + 0x36, 0xc1, 0x60, 0x14, 0x2e, 0x08, 0xa6, 0xe3, 0x92, 0x60, 0x38, 0xb8, + 0x22, 0x5c, 0x10, 0x0c, 0x82, 0x41, 0x30, 0x88, 0xc1, 0xe4, 0x27, 0xb0, + 0xf8, 0x8d, 0x0c, 0x1e, 0xf7, 0xdf, 0xd9, 0xb0, 0xbb, 0xf9, 0xc7, 0xbe, + 0x99, 0xe1, 0x71, 0x92, 0x2d, 0x9b, 0x99, 0xa5, 0x94, 0x54, 0x55, 0xa3, + 0x4a, 0x14, 0x11, 0x09, 0x21, 0x90, 0x24, 0xc8, 0xdb, 0x51, 0x47, 0xdc, + 0x58, 0x5b, 0xf0, 0x3a, 0x77, 0xc4, 0xa5, 0x75, 0xc4, 0x85, 0x75, 0xc4, + 0x99, 0x75, 0xc4, 0x69, 0x6a, 0x0b, 0x9e, 0x98, 0x13, 0xe7, 0xf8, 0xf6, + 0x82, 0xc7, 0xa9, 0x14, 0x57, 0xc0, 0x97, 0xff, 0x83, 0x47, 0x5a, 0x88, + 0x31, 0x80, 0x0f, 0x9f, 0xc2, 0xc3, 0x34, 0x88, 0x29, 0x00, 0xbc, 0xf9, + 0x39, 0x78, 0xa0, 0x2b, 0x31, 0x07, 0x66, 0x63, 0xbc, 0xf8, 0x49, 0xb9, + 0xaf, 0x2b, 0xb1, 0xc0, 0x24, 0xdf, 0xe1, 0xc9, 0xef, 0xc2, 0xbd, 0x38, + 0x88, 0x6c, 0xa3, 0x29, 0x1e, 0xfd, 0xb6, 0xdc, 0x55, 0x27, 0x1e, 0x30, + 0xf3, 0xf7, 0xe0, 0x4e, 0x74, 0x62, 0x8e, 0x7b, 0x7f, 0x31, 0x6e, 0x47, + 0x27, 0x16, 0xa8, 0x1a, 0xc4, 0x2d, 0x71, 0xe2, 0x19, 0x55, 0x83, 0xb8, + 0x29, 0x4e, 0xbc, 0xa2, 0x6a, 0x10, 0x37, 0xa2, 0x13, 0xef, 0xa8, 0x1a, + 0xc4, 0xf5, 0xe0, 0xc4, 0x27, 0xaa, 0x06, 0x71, 0x4d, 0x06, 0xf1, 0x83, + 0xff, 0x57, 0xa6, 0x30, 0x87, 0x41, 0xfc, 0x2e, 0x41, 0x99, 0xc2, 0x14, + 0x0a, 0x51, 0xde, 0x74, 0x29, 0x18, 0x43, 0x5b, 0x50, 0xd8, 0x16, 0x0c, + 0x6c, 0x0b, 0x92, 0x6d, 0x41, 0x74, 0x04, 0xd1, 0x11, 0x44, 0x47, 0xfc, + 0x01, 0x95, 0x51, 0x2c, 0x83, 0x13, 0x7f, 0xab, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +]; + +const filter_sub_data: [_]u8 = [ + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xfe, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0x00, 0x00, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0x00, 0x00, 0x00, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, + 0x00, 0x00, 0x8e, 0x00, 0x00, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x00, 0x00, 0x7f, 0x87, 0x00, + 0x00, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x00, 0x00, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x00, 0x00, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, + 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x00, 0x00, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x00, 0x00, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x00, + 0x00, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x00, 0x00, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x00, 0x00, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x01, 0x02, 0x03, 0x04, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, +]; + +const filter_up_png: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x56, 0x11, 0x25, 0x28, 0x00, 0x00, 0x01, + 0x2a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x85, 0xd1, 0x2f, 0x4b, 0x83, + 0x51, 0x18, 0x05, 0xf0, 0xc3, 0xf9, 0x38, 0x16, 0x8b, 0x20, 0x88, 0x20, + 0x08, 0x82, 0x41, 0x0c, 0x03, 0x83, 0x4d, 0xd8, 0x07, 0x18, 0x2c, 0x58, + 0x87, 0x49, 0x30, 0x0c, 0x56, 0x04, 0xc3, 0x0b, 0x0b, 0x03, 0xc3, 0x60, + 0xe1, 0x4d, 0x82, 0x60, 0x12, 0xc1, 0x62, 0xb1, 0x18, 0x2c, 0x82, 0x41, + 0x10, 0x04, 0x41, 0xf4, 0x7d, 0xee, 0x5f, 0xc3, 0xa6, 0xf7, 0xb9, 0xef, + 0xc2, 0xbd, 0xf9, 0xc7, 0xe5, 0x9c, 0xf3, 0x70, 0x70, 0x3a, 0x3c, 0xaf, + 0x26, 0xd3, 0xfa, 0xea, 0xe6, 0xf6, 0xfe, 0xe1, 0xf1, 0xe9, 0xf9, 0xe5, + 0xf5, 0xed, 0xfd, 0xe3, 0xf3, 0xeb, 0xbb, 0x31, 0xd6, 0xf9, 0x10, 0x22, + 0x7f, 0x1a, 0x69, 0x44, 0x44, 0x8c, 0x31, 0xd6, 0x5a, 0xeb, 0xac, 0x77, + 0xde, 0x7b, 0x1f, 0x42, 0x88, 0x31, 0x46, 0x44, 0x4a, 0x41, 0xb0, 0x29, + 0x08, 0x4a, 0x41, 0x50, 0x0a, 0x82, 0x52, 0x10, 0x34, 0x05, 0x41, 0xd1, + 0xe2, 0x60, 0x7f, 0x77, 0x7b, 0x73, 0x3d, 0x13, 0x34, 0x4a, 0x74, 0x01, + 0x00, 0xc8, 0x04, 0x6d, 0x12, 0x7d, 0xa0, 0x9a, 0x4c, 0xeb, 0x5c, 0xd0, + 0x24, 0x01, 0x0c, 0x45, 0x44, 0x6a, 0x40, 0xe5, 0xa0, 0x4d, 0x02, 0x98, + 0x27, 0x05, 0x54, 0x52, 0xda, 0x24, 0x7a, 0xc7, 0xf3, 0x2e, 0x80, 0xea, + 0x42, 0xa7, 0xc4, 0xa2, 0x0b, 0xa0, 0xda, 0xd2, 0x2e, 0x89, 0x2e, 0xa0, + 0xf6, 0xa0, 0x6b, 0x8b, 0x3e, 0x50, 0xa9, 0xc5, 0xe8, 0x5a, 0xe2, 0x64, + 0xd1, 0xe5, 0x4f, 0xd0, 0xe7, 0x62, 0x04, 0x0c, 0xb2, 0xd5, 0xe9, 0x33, + 0x31, 0x06, 0x7a, 0xf9, 0x5d, 0xe8, 0xb4, 0x98, 0x01, 0x47, 0xad, 0xcb, + 0x31, 0x28, 0x71, 0x0d, 0x74, 0xda, 0xb7, 0xa5, 0x4f, 0xe2, 0x0e, 0xc0, + 0xec, 0x72, 0x7c, 0x31, 0x3a, 0x53, 0x82, 0x21, 0x09, 0xfc, 0x3f, 0xf5, + 0x07, 0x43, 0x12, 0x2b, 0xab, 0x6b, 0x1b, 0x5b, 0x3b, 0x7b, 0x9d, 0x43, + 0x9d, 0x83, 0x41, 0x89, 0xe5, 0xd5, 0x45, 0x18, 0x0b, 0x82, 0xb1, 0x20, + 0x18, 0x0b, 0x82, 0xb1, 0x20, 0x88, 0x82, 0x20, 0x0a, 0xe2, 0x17, 0xe9, + 0xe4, 0x97, 0xbf, 0xbf, 0xcc, 0xeb, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +]; + +const filter_up_data: [_]u8 = [ + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xfe, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x00, 0x00, 0x00, 0x9d, 0xa4, + 0xab, 0xb2, 0x00, 0x00, 0x00, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x00, 0x00, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0x00, + 0x00, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, + 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0x00, 0x00, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0x00, 0x00, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x00, + 0x00, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x00, 0x00, 0x00, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x00, 0x00, 0x00, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, 0x00, 0x00, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x00, 0x00, 0x00, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x00, 0x00, + 0x00, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x00, 0x00, 0x00, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x00, 0x00, 0x00, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x01, 0x02, 0x03, 0x04, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, +]; + +const filter_avg_png: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x56, 0x11, 0x25, 0x28, 0x00, 0x00, 0x01, + 0x4c, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x7d, 0xd2, 0xb1, 0x4b, 0x42, + 0x51, 0x14, 0xc7, 0xf1, 0xef, 0xe9, 0x84, 0x44, 0x51, 0x10, 0x11, 0x0d, + 0x21, 0x45, 0x82, 0x50, 0x14, 0x34, 0x04, 0x2e, 0x0e, 0x41, 0xcb, 0x03, + 0x07, 0xa1, 0x21, 0x08, 0x8a, 0x22, 0x10, 0x21, 0x03, 0x83, 0xa2, 0x48, + 0xcc, 0xe7, 0xda, 0x5f, 0x10, 0x42, 0x05, 0x41, 0xd0, 0x20, 0x38, 0x34, + 0xb5, 0x38, 0x08, 0x35, 0x34, 0x04, 0x0d, 0x41, 0xe0, 0x1f, 0xe0, 0xbf, + 0xf1, 0x1a, 0xd4, 0xe7, 0x79, 0xca, 0x7b, 0x77, 0xb9, 0x1c, 0xf8, 0x70, + 0xef, 0xb9, 0xf7, 0x77, 0xd4, 0x75, 0x32, 0xd9, 0xdd, 0xbd, 0xfd, 0xc3, + 0xa3, 0x93, 0x5c, 0xfe, 0xf4, 0xac, 0x78, 0x7e, 0x71, 0x79, 0x75, 0x7d, + 0x53, 0x2a, 0x97, 0x6f, 0x2b, 0x15, 0xd7, 0xad, 0xba, 0x55, 0x4d, 0x89, + 0x20, 0x80, 0xd0, 0xdb, 0x86, 0x4a, 0xdd, 0xf2, 0xa2, 0x85, 0x6e, 0x4a, + 0xb4, 0xd0, 0x0d, 0xa2, 0x85, 0xae, 0x12, 0x2d, 0x34, 0x49, 0xb4, 0xd0, + 0x04, 0xd1, 0x42, 0x97, 0xc4, 0x17, 0x85, 0x46, 0xfd, 0xf5, 0xe5, 0xf9, + 0xe9, 0xa1, 0x76, 0x9f, 0x1f, 0x08, 0x8d, 0xe3, 0x8b, 0x26, 0xfd, 0x95, + 0x1b, 0x9c, 0xa1, 0x8b, 0xf8, 0xa2, 0xfb, 0xa7, 0x75, 0xf8, 0x30, 0xb7, + 0xe8, 0x82, 0x0c, 0x84, 0x20, 0x1c, 0xc0, 0xbb, 0xed, 0x43, 0xe7, 0x09, + 0x88, 0x02, 0xd4, 0x03, 0x9d, 0xea, 0x1c, 0x56, 0x94, 0xe0, 0x31, 0xf8, + 0x16, 0x9d, 0xc5, 0x88, 0x3b, 0xe0, 0xad, 0x91, 0xb5, 0x42, 0x67, 0xcc, + 0xf3, 0xf9, 0xc6, 0x75, 0x9a, 0x04, 0x84, 0x4e, 0xdb, 0x0f, 0x4a, 0xa5, + 0x3d, 0x19, 0x12, 0x3a, 0x85, 0x15, 0x88, 0x27, 0x4e, 0x93, 0x8c, 0x89, + 0x7b, 0x92, 0x11, 0xd1, 0xc2, 0x31, 0x71, 0x4f, 0x30, 0x22, 0x3e, 0xd9, + 0x31, 0x71, 0xc7, 0xc4, 0x17, 0xad, 0x76, 0x37, 0xb9, 0x2f, 0xb6, 0x4d, + 0xdc, 0x31, 0xcf, 0x17, 0x1d, 0xda, 0x09, 0xa0, 0x06, 0x69, 0x13, 0xf7, + 0xb8, 0xf8, 0x22, 0xde, 0xa1, 0xfd, 0xf7, 0xfb, 0x03, 0x45, 0x93, 0xbe, + 0x2a, 0x01, 0x01, 0x70, 0x6c, 0xe7, 0x43, 0xc7, 0xb0, 0x62, 0x79, 0x25, + 0xb9, 0xb6, 0x1e, 0x98, 0x20, 0x15, 0xac, 0x08, 0x26, 0x27, 0x80, 0xf6, + 0xcb, 0x30, 0xd1, 0x05, 0x11, 0x42, 0x7b, 0x43, 0x16, 0x2a, 0xfa, 0x20, + 0x54, 0xf8, 0x20, 0x4c, 0x0c, 0x40, 0x88, 0xf8, 0x07, 0x63, 0x67, 0x5c, + 0x42, 0xbc, 0xfe, 0x0b, 0x39, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82, +]; + +const filter_avg_data: [_]u8 = [ + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xfe, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0x00, 0x00, 0x00, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x00, 0x00, 0x00, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x00, 0x00, 0x00, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x00, 0x00, + 0x00, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x00, 0x00, 0x7f, 0x87, 0x00, 0x00, 0x00, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x00, 0x00, 0x00, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x00, + 0x00, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x00, 0x00, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x00, 0x00, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x00, 0x00, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x00, + 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x00, + 0x00, 0x00, 0x4c, 0x52, 0x59, 0x60, 0x00, 0x00, 0x00, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x01, 0x02, 0x03, 0x04, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, +]; + +const filter_peath_png: [_]u8 = [ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x56, 0x11, 0x25, 0x28, 0x00, 0x00, 0x00, + 0xd4, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x85, 0xd1, 0xc1, 0x6d, 0xc2, + 0x30, 0x14, 0x80, 0xe1, 0xdf, 0xf6, 0xb3, 0xcd, 0x4c, 0x9d, 0xa1, 0x62, + 0x06, 0xa4, 0x0e, 0xc0, 0x0d, 0x29, 0xa7, 0x6e, 0x80, 0xd4, 0x4b, 0x27, + 0xa8, 0x94, 0x2d, 0x58, 0x0b, 0x42, 0x21, 0xe6, 0xc0, 0x01, 0xdb, 0x0f, + 0xdb, 0x39, 0x44, 0x7a, 0xd1, 0xa7, 0xff, 0xd9, 0x8a, 0x7c, 0x6f, 0xe2, + 0x26, 0xc6, 0x18, 0x43, 0x08, 0xde, 0x7b, 0x2f, 0xde, 0x89, 0x73, 0xce, + 0x59, 0x6b, 0x8d, 0x31, 0x06, 0x23, 0x67, 0x5a, 0x8f, 0x05, 0x4c, 0x92, + 0xa5, 0x09, 0x9e, 0x42, 0x2e, 0x6d, 0x80, 0x05, 0x7a, 0x05, 0xb0, 0x23, + 0x80, 0x1d, 0x01, 0xac, 0x5c, 0xfb, 0x40, 0x15, 0xb6, 0x9c, 0x2a, 0xa0, + 0x0a, 0xd5, 0x07, 0xf9, 0x2f, 0xc6, 0x2f, 0x0d, 0xea, 0xc2, 0x5f, 0xb5, + 0xb3, 0x2c, 0x4c, 0x50, 0xdf, 0xaa, 0x5a, 0xc1, 0x51, 0x81, 0x5b, 0x36, + 0xfc, 0xe8, 0x40, 0x5d, 0x38, 0xa8, 0x5f, 0x93, 0x17, 0x66, 0x58, 0x60, + 0xe2, 0xb7, 0xb9, 0x82, 0xdd, 0x05, 0xa0, 0xa8, 0xc8, 0x3d, 0x9f, 0x96, + 0xec, 0xfd, 0x16, 0xcc, 0x00, 0x4c, 0x1c, 0x5b, 0x2b, 0xca, 0xd2, 0x13, + 0xac, 0x2f, 0xf1, 0xe1, 0x43, 0x0c, 0x71, 0x66, 0x1f, 0xb3, 0x53, 0xc8, + 0x1d, 0xd5, 0x28, 0x0f, 0xb9, 0xa2, 0xc4, 0x52, 0x83, 0x52, 0x7c, 0x06, + 0x0d, 0xf4, 0x96, 0x0c, 0xa4, 0x81, 0x90, 0x44, 0x5f, 0x48, 0xa2, 0x2f, + 0x24, 0xd1, 0x17, 0xc2, 0x40, 0x08, 0x03, 0xf1, 0x00, 0x16, 0x26, 0x4c, + 0x07, 0x21, 0x99, 0x60, 0x81, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82, +]; + +const filter_peath_data: [_]u8 = [ + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xfe, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0xf8, 0xfa, 0xfb, 0xfc, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, 0xf8, 0xfa, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0xf1, 0xf4, 0xf6, 0xf8, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0x00, 0x00, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0xf6, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0x00, 0x00, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, 0xf1, 0xf4, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x00, 0x00, + 0x00, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0xe8, 0xeb, 0xee, 0xf1, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x00, 0x00, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0xee, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x00, 0x00, 0x00, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, 0xe8, 0xeb, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x00, 0x00, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0xdc, 0xe1, 0xe4, 0xe8, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x00, 0x00, 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0xe4, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x00, 0x00, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, 0xdc, 0xe1, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x00, 0x00, 0x00, 0x6f, 0x76, + 0x7f, 0x00, 0x00, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0xce, 0xd3, 0xd8, 0xdc, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x00, 0x00, 0x60, 0x67, 0x6f, 0x76, 0x00, 0x00, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0xd8, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x00, + 0x00, 0x59, 0x60, 0x67, 0x6f, 0x00, 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, 0xce, 0xd3, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0xbe, 0xc4, 0xc9, 0xce, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0xc9, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x00, 0x00, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, 0xbe, 0xc4, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x00, 0x00, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0xab, 0xb2, 0xb8, 0xbe, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x00, 0x00, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb8, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x96, 0x9d, 0xa4, 0xab, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0xa4, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, 0x96, 0x9d, 0x01, 0x02, 0x03, 0x04, + 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, + 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, + 0x7f, 0x87, 0x8e, 0x96, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, + 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, + 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x8e, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, + 0x16, 0x19, 0x1d, 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, + 0x52, 0x59, 0x60, 0x67, 0x6f, 0x76, 0x7f, 0x87, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x06, 0x08, 0x0a, 0x0d, 0x0f, 0x13, 0x16, 0x19, 0x1d, + 0x21, 0x26, 0x2a, 0x2f, 0x34, 0x3a, 0x3f, 0x45, 0x4c, 0x52, 0x59, 0x60, + 0x67, 0x6f, 0x76, 0x7f, +]; diff --git a/image/png/types.ha b/image/png/types.ha new file mode 100644 index 0000000..b970175 --- /dev/null +++ b/image/png/types.ha @@ -0,0 +1,69 @@ +// The PNG magic number. +export const magic: [_]u8 = [137, 80, 78, 71, 13, 10, 26, 10]; + +def MAGIC_LEN: size = 8; + +// The chunk header type for an IHDR chunk. +export def IHDR: u32 = 0x49484452; + +// The chunk header type for a PLTE chunk. +export def PLTE: u32 = 0x504c5445; + +// The chunk header type for an IDAT chunk. +export def IDAT: u32 = 0x49444154; + +// The chunk header type for an IEND chunk. +export def IEND: u32 = 0x49454e44; + +// The chunk header type for a tRNS chunk. +export def TRNS: u32 = 0x74524e53; + +// The chunk header type for a gAMA chunk. +export def GAMA: u32 = 0x67414d41; + +// The chunk header type for an sRGB chunk. +export def SRGB: u32 = 0x73524742; + +// The chunk header type for an iCCP chunk. +export def ICCP: u32 = 0x69434350; + +// The chunk header type for a tEXT chunk. +export def TEXT: u32 = 0x74455854; + +// The chunk header type for a zTXT chunk. +export def ZTXT: u32 = 0x7a545854; + +// The chunk header type for an iTXT chunk. +export def ITXT: u32 = 0x69545854; + +// The chunk header type for a bKGD chunk. +export def BKGD: u32 = 0x624b4744; + +// The chunk header type for a pHYS chunk. +export def PHYS: u32 = 0x70485953; + +// The chunk header type for a sBIT chunk. +export def SBIT: u32 = 0x73424954; + +// The chunk header type for a sPLT chunk. +export def SPLT: u32 = 0x73504c54; + +// The chunk header type for a hIST chunk. +export def HIST: u32 = 0x68495354; + +// The chunk header type for a tIME chunk. +export def TIME: u32 = 0x74494d45; + +// Returns true if the given chunk type is a "critical" chunk. +export fn is_critical(ctype: u32) bool = { + return ctype & (1 << 29) == 0; +}; + +@test fn is_critical() void = { + assert(is_critical(IHDR) == true); + assert(is_critical(PLTE) == true); + assert(is_critical(IDAT) == true); + assert(is_critical(IEND) == true); + assert(is_critical(HIST) == false); + assert(is_critical(TIME) == false); +}; |