Мы плохо работаем с RGB изображениями. На изображении после инверсии цвета артефакты. Исправь png.h . Выдай целиком, полный рабочий код.
Результат:
# v# 1258 f# 2492
Цветовой тип: RGB
PNG файл успешно загружен: image.png
Изображение загружено: 1678x975
PNG файл успешно сохранен: output2.png
отрывок main:
 // Чтение изображения из файла
 if (pngImage.read("image.png")) {
     png::Image& img = pngImage.getImage();
     std::cout << "Изображение загружено: " << img.width << "x" << img.height << "\n";
     // инверсия цветов
     for (size_t i = 0; i < img.data.size(); i += 4) {
         img.data[i + 0] = 255 - img.data[i + 0]; // R
         img.data[i + 1] = 255 - img.data[i + 1]; // G
         img.data[i + 2] = 255 - img.data[i + 2]; // B
     }
     pngImage.save("output2.png");
 }
 delete model;
 delete[] zbuffer;
png.h:
#pragma once
#include <cstdint>
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <cstring>
#include <algorithm>
#include "zlib/zlib.h"
namespace png {
    // Структура для хранения изображения
    struct Image {
        uint32_t width;
        uint32_t height;
        uint8_t channels; // 3 для RGB, 4 для RGBA
        std::vector<uint8_t> data; // Формат: RGB или RGBA
    };
    // Класс для чтения и записи PNG-файлов
    class PngImage {
    public:
        // Конструктор
        PngImage() : width_(0), height_(0), bit_depth_(8), color_type_(6), channels_(4) {}
        // Функция чтения PNG изображения из файла
        bool read(const std::string& filename) {
            std::ifstream file(filename, std::ios::binary);
            if (!file) {
                std::cerr << "Не удалось открыть файл для чтения: " << filename << "\n";
                return false;
            }
            // Проверяем сигнатуру PNG
            uint8_t png_signature[8];
            file.read(reinterpret_cast<char*>(png_signature), 8);
            uint8_t expected_signature[8] = { 0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n' };
            if (memcmp(png_signature, expected_signature, 8) != 0) {
                std::cerr << "Файл не является PNG изображением.\n";
                return false;
            }
            std::vector<uint8_t> idat_data;
            bool ihdr_read = false;
            // Читаем чанки
            while (true) {
                uint32_t chunk_length = 0;
                if (!file.read(reinterpret_cast<char*>(&chunk_length), 4)) break;
                chunk_length = swapEndian(chunk_length);
                char chunk_type[5] = { 0 };
                if (!file.read(chunk_type, 4)) break;
                if (chunk_length > 0x7fffffff) {
                    std::cerr << "Некорректная длина чанка: " << chunk_length << ".\n";
                    return false;
                }
                std::vector<uint8_t> chunk_data(chunk_length);
                if (chunk_length > 0) {
                    if (!file.read(reinterpret_cast<char*>(chunk_data.data()), chunk_length)) break;
                }
                uint32_t crc = 0;
                if (!file.read(reinterpret_cast<char*>(&crc), 4)) break;
                crc = swapEndian(crc);
                uint32_t computed_crc = crc32(0L, Z_NULL, 0);
                computed_crc = crc32(computed_crc, reinterpret_cast<const uint8_t*>(chunk_type), 4);
                if (chunk_length > 0) {
                    computed_crc = crc32(computed_crc, chunk_data.data(), chunk_length);
                }
                if (crc != computed_crc) {
                    std::cerr << "CRC ошибка в чанке " << chunk_type << ".\n";
                    return false;
                }
                if (strcmp(chunk_type, "IHDR") == 0) {
                    if (ihdr_read) {
                        std::cerr << "Найдено более одного чанка IHDR.\n";
                        return false;
                    }
                    ihdr_read = true;
                    if (chunk_length != 13) {
                        std::cerr << "Некорректная длина чанка IHDR.\n";
                        return false;
                    }
                    // Безопасно читаем значения из chunk_data
                    width_ = readUInt32BE(chunk_data.data());
                    height_ = readUInt32BE(chunk_data.data() + 4);
                    bit_depth_ = chunk_data[8];
                    color_type_ = chunk_data[9];
                    if (bit_depth_ != 8) {
                        std::cerr << "Поддерживаются только 8-битные изображения.\n";
                        return false;
                    }
                    if (color_type_ == 2) {
                        std::cout << "\nЦветовой тип: RGB\n";
                        channels_ = 3; // RGB
                    }
                    else if (color_type_ == 6) {
                        std::cout << "\nЦветовой тип: RGBA\n";
                        channels_ = 4; // RGBA
                    }
                    else {
                        std::cerr << "Поддерживаются только цветовые типы RGB и RGBA.\n";
                        return false;
                    }
                }
                else if (strcmp(chunk_type, "IDAT") == 0) {
                    idat_data.insert(idat_data.end(), chunk_data.begin(), chunk_data.end());
                }
                else if (strcmp(chunk_type, "IEND") == 0) {
                    break;
                }
                else {
                    // Пропускаем другие чанки
                    continue;
                }
            }
            if (!ihdr_read) {
                std::cerr << "Не найден чанк IHDR.\n";
                return false;
            }
            std::vector<uint8_t> decompressed_data;
            if (!decompressData(idat_data, decompressed_data)) {
                std::cerr << "Ошибка при распаковке данных изображения.\n";
                return false;
            }
            if (!processImageData(decompressed_data)) {
                std::cerr << "Ошибка при обработке данных изображения.\n";
                return false;
            }
            std::cout << "PNG файл успешно загружен: " << filename << "\n";
            return true;
        }
        // Функция сохранения PNG изображения в файл
        bool save(const std::string& filename) {
            if (image_.width == 0 || image_.height == 0 || image_.data.empty()) {
                std::cerr << "Данные изображения не инициализированы.\n";
                return false;
            }
            std::ofstream file(filename, std::ios::binary);
            if (!file) {
                std::cerr << "Не удалось открыть файл для записи: " << filename << "\n";
                return false;
            }
            uint8_t png_signature[8] = { 0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n' };
            file.write(reinterpret_cast<char*>(png_signature), 8);
            uint8_t ihdr_data[13];
            writeUInt32BE(ihdr_data, image_.width);
            writeUInt32BE(ihdr_data + 4, image_.height);
            ihdr_data[8] = 8;  // Bit depth
            ihdr_data[9] = (image_.channels == 4) ? 6 : 2;  // Color type (RGBA or RGB)
            ihdr_data[10] = 0; // Compression method
            ihdr_data[11] = 0; // Filter method
            ihdr_data[12] = 0; // Interlace method
            writeChunk(file, "IHDR", ihdr_data, 13);
            std::vector<uint8_t> filtered_data;
            if (!prepareImageData(filtered_data)) {
                std::cerr << "Ошибка при подготовке данных изображения.\n";
                return false;
            }
            std::vector<uint8_t> compressed_data;
            if (!compressData(filtered_data, compressed_data)) {
                std::cerr << "Ошибка при сжатии данных изображения.\n";
                return false;
            }
            writeChunk(file, "IDAT", compressed_data.data(), compressed_data.size());
            writeChunk(file, "IEND", nullptr, 0);
            file.close();
            std::cout << "PNG файл успешно сохранен: " << filename << "\n";
            return true;
        }
        void setImage(const Image& image) {
            image_ = image;
            width_ = image_.width;
            height_ = image_.height;
            bit_depth_ = 8;
            channels_ = image_.channels;
            color_type_ = (channels_ == 4) ? 6 : 2;
        }
        Image& getImage() {
            return image_;
        }
        void flipVertically() {
            if (image_.data.empty()) {
                std::cerr << "Данные изображения не инициализированы.\n";
                return;
            }
            size_t stride = width_ * channels_;
            size_t half_height = height_ / 2;
            for (size_t y = 0; y < half_height; ++y) {
                size_t top = y * stride;
                size_t bottom = (height_ - 1 - y) * stride;
                std::swap_ranges(image_.data.begin() + top, image_.data.begin() + top + stride, image_.data.begin() + bottom);
            }
        }
        void setPixel(uint32_t x, uint32_t y, uint32_t color) {
            if (x >= image_.width || y >= image_.height) {
                std::cerr << "Координаты пикселя (" << x << ", " << y << ") выходят за границы изображения.\n";
                return;
            }
            size_t index = (y * image_.width + x) * image_.channels;
            if (image_.channels == 4) {
                // RGBA: цвет в формате 0xRRGGBBAA
                image_.data[index + 0] = (color >> 24) & 0xFF; // R
                image_.data[index + 1] = (color >> 16) & 0xFF; // G
                image_.data[index + 2] = (color >> 8) & 0xFF;  // B
                image_.data[index + 3] = color & 0xFF;         // A
            }
            else if (image_.channels == 3) {
                // RGB: цвет в формате 0xRRGGBB
                image_.data[index + 0] = (color >> 16) & 0xFF; // R
                image_.data[index + 1] = (color >> 8) & 0xFF;  // G
                image_.data[index + 2] = color & 0xFF;         // B
            }
        }
        void initialize(uint32_t width, uint32_t height, bool has_alpha = true) {
            width_ = width;
            height_ = height;
            bit_depth_ = 8;
            channels_ = has_alpha ? 4 : 3;
            color_type_ = has_alpha ? 6 : 2;
            image_.width = width;
            image_.height = height;
            image_.channels = channels_;
            image_.data.resize(width * height * channels_, 0);
        }
    private:
        // Чтение 32-битного значения в big-endian
        uint32_t readUInt32BE(const uint8_t* data) const {
            return (static_cast<uint32_t>(data[0]) << 24) |
                (static_cast<uint32_t>(data[1]) << 16) |
                (static_cast<uint32_t>(data[2]) << 8) |
                static_cast<uint32_t>(data[3]);
        }
        // Запись 32-битного значения в big-endian
        void writeUInt32BE(uint8_t* data, uint32_t value) const {
            data[0] = (value >> 24) & 0xFF;
            data[1] = (value >> 16) & 0xFF;
            data[2] = (value >> 8) & 0xFF;
            data[3] = value & 0xFF;
        }
        bool decompressData(const std::vector<uint8_t>& input, std::vector<uint8_t>& output) {
            z_stream strm = {};
            strm.next_in = const_cast<Bytef*>(input.data());
            strm.avail_in = input.size();
            if (inflateInit(&strm) != Z_OK) {
                return false;
            }
            const size_t bufferSize = 16384;
            uint8_t buffer[bufferSize];
            int ret;
            do {
                strm.next_out = buffer;
                strm.avail_out = bufferSize;
                ret = inflate(&strm, Z_NO_FLUSH);
                if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
                    inflateEnd(&strm);
                    return false;
                }
                size_t have = bufferSize - strm.avail_out;
                output.insert(output.end(), buffer, buffer + have);
            } while (ret != Z_STREAM_END);
            inflateEnd(&strm);
            return ret == Z_STREAM_END;
        }
        bool processImageData(const std::vector<uint8_t>& data) {
            size_t bytesPerPixel = channels_;
            size_t stride = width_ * bytesPerPixel;
            image_.width = width_;
            image_.height = height_;
            image_.channels = channels_;
            image_.data.resize(width_ * height_ * bytesPerPixel);
            size_t src_offset = 0;
            size_t dst_offset = 0;
            for (uint32_t y = 0; y < height_; ++y) {
                if (src_offset >= data.size()) {
                    std::cerr << "Размер данных меньше ожидаемого.\n";
                    return false;
                }
                uint8_t filter_type = data[src_offset++];
                const uint8_t* src_line = &data[src_offset];
                uint8_t* dst_line = &image_.data[dst_offset];
                if (src_offset + stride > data.size()) {
                    std::cerr << "Несоответствие размера данных.\n";
                    return false;
                }
                switch (filter_type) {
                case 0:
                    memcpy(dst_line, src_line, stride);
                    break;
                case 1:
                    for (size_t i = 0; i < stride; ++i) {
                        uint8_t val = src_line[i];
                        uint8_t left = (i >= bytesPerPixel) ? dst_line[i - bytesPerPixel] : 0;
                        dst_line[i] = val + left;
                    }
                    break;
                case 2:
                    for (size_t i = 0; i < stride; ++i) {
                        uint8_t val = src_line[i];
                        uint8_t up = (y > 0) ? image_.data[dst_offset - stride + i] : 0;
                        dst_line[i] = val + up;
                    }
                    break;
                case 3:
                    for (size_t i = 0; i < stride; ++i) {
                        uint8_t val = src_line[i];
                        uint8_t left = (i >= bytesPerPixel) ? dst_line[i - bytesPerPixel] : 0;
                        uint8_t up = (y > 0) ? image_.data[dst_offset - stride + i] : 0;
                        dst_line[i] = val + static_cast<uint8_t>((left + up) / 2);
                    }
                    break;
                case 4:
                    for (size_t i = 0; i < stride; ++i) {
                        uint8_t val = src_line[i];
                        uint8_t left = (i >= bytesPerPixel) ? dst_line[i - bytesPerPixel] : 0;
                        uint8_t up = (y > 0) ? image_.data[dst_offset - stride + i] : 0;
                        uint8_t up_left = (y > 0 && i >= bytesPerPixel) ? image_.data[dst_offset - stride + i - bytesPerPixel] : 0;
                        dst_line[i] = val + paethPredictor(left, up, up_left);
                    }
                    break;
                default:
                    std::cerr << "Неизвестный тип фильтра: " << static_cast<int>(filter_type) << "\n";
                    return false;
                }
                src_offset += stride;
                dst_offset += stride;
            }
            return true;
        }
        bool prepareImageData(std::vector<uint8_t>& output) {
            size_t bytesPerPixel = channels_;
            size_t stride = image_.width * bytesPerPixel;
            size_t total_size = image_.height * (stride + 1);
            output.resize(total_size);
            size_t src_offset = 0;
            size_t dst_offset = 0;
            for (uint32_t y = 0; y < image_.height; ++y) {
                output[dst_offset++] = 0; // Фильтр None (0)
                memcpy(&output[dst_offset], &image_.data[src_offset], stride);
                src_offset += stride;
                dst_offset += stride;
            }
            return true;
        }
        bool compressData(const std::vector<uint8_t>& input, std::vector<uint8_t>& output) {
            z_stream strm = {};
            if (deflateInit(&strm, Z_BEST_COMPRESSION) != Z_OK) {
                return false;
            }
            strm.next_in = const_cast<Bytef*>(input.data());
            strm.avail_in = input.size();
            const size_t bufferSize = 16384;
            uint8_t buffer[bufferSize];
            int ret;
            do {
                strm.next_out = buffer;
                strm.avail_out = bufferSize;
                ret = deflate(&strm, Z_FINISH);
                if (ret == Z_STREAM_ERROR) {
                    deflateEnd(&strm);
                    return false;
                }
                size_t have = bufferSize - strm.avail_out;
                output.insert(output.end(), buffer, buffer + have);
            } while (ret != Z_STREAM_END);
            deflateEnd(&strm);
            return ret == Z_STREAM_END;
        }
        void writeChunk(std::ofstream& file, const char* type, const uint8_t* data, size_t length) {
            uint32_t length_be = swapEndian(static_cast<uint32_t>(length));
            file.write(reinterpret_cast<char*>(&length_be), 4);
            file.write(type, 4);
            if (data && length > 0) {
                file.write(reinterpret_cast<const char*>(data), length);
            }
            uint32_t crc = crc32(0L, Z_NULL, 0);
            crc = crc32(crc, reinterpret_cast<const unsigned char*>(type), 4);
            if (length > 0 && data) {
                crc = crc32(crc, data, length);
            }
            uint32_t crc_be = swapEndian(crc);
            file.write(reinterpret_cast<char*>(&crc_be), 4);
        }
        uint8_t paethPredictor(int a, int b, int c) const {
            int p = a + b - c;
            int pa = abs(p - a);
            int pb = abs(p - b);
            int pc = abs(p - c);
            if (pa <= pb && pa <= pc)
                return static_cast<uint8_t>(a);
            else if (pb <= pc)
                return static_cast<uint8_t>(b);
            else
                return static_cast<uint8_t>(c);
        }
        uint32_t swapEndian(uint32_t val) const {
            return ((val & 0x000000FF) << 24) |
                ((val & 0x0000FF00) << 8) |
                ((val & 0x00FF0000) >> 8) |
                ((val & 0xFF000000) >> 24);
        }
        uint32_t width_;
        uint32_t height_;
        uint8_t bit_depth_;
        uint8_t color_type_;
        uint8_t channels_; // 3 для RGB, 4 для RGBA
        Image image_;
    };
} // namespace png