summaryrefslogtreecommitdiff
path: root/image
diff options
context:
space:
mode:
Diffstat (limited to 'image')
-rw-r--r--image/README0
-rw-r--r--image/png/COPYING367
-rw-r--r--image/png/README8
-rw-r--r--image/png/decoder.ha171
-rw-r--r--image/png/errors.ha35
-rw-r--r--image/png/filter.ha75
-rw-r--r--image/png/idat.ha82
-rw-r--r--image/png/iend.ha27
-rw-r--r--image/png/ihdr.ha114
-rw-r--r--image/png/load.ha88
-rw-r--r--image/png/paeth.ha30
-rw-r--r--image/png/plte.ha95
-rw-r--r--image/png/reader.ha175
-rw-r--r--image/png/test_data.ha638
-rw-r--r--image/png/types.ha69
15 files changed, 1974 insertions, 0 deletions
diff --git a/image/README b/image/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/image/README
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);
+};