/* Reject unsafe conversions before entering any encoder. */
static int saveimage_checked_int(double value) {
    double integer = trunc(value);
    if (!isfinite(integer) || integer < (double)INT_MIN || integer > (double)INT_MAX)
        runtime_error("SAVEIMAGE failed: numeric argument out of range / 【画像保存】数値引数が処理可能な範囲を超えています。");
    return (int)integer;
}
static int saveimage_size_valid(int width, int height) {
    size_t pixels;
    if (width <= 0 || height <= 0) return 0;
    /* Encoders use signed int lengths; reserve space for rows and headers.
       The largest per-pixel intermediate is 16 bytes (32-bit RGBA PSD). */
    if ((size_t)width > (size_t)(INT_MAX - 1024) / 16U / (size_t)height) return 0;
    pixels = (size_t)width * (size_t)height;
    return pixels <= SIZE_MAX / 32U;
}
static int saveimage_format_size_valid(const char *path, int width, int height) {
    const char *ext = path != NULL ? strrchr(path, '.') : NULL;
    int limit = INT_MAX;
    if (ext == NULL) return 1;
    if (b2026_strcasecmp(ext, ".mag") == 0) return width <= 65528 && height <= 65535;
    if (b2026_strcasecmp(ext, ".mzg") == 0 || b2026_strcasecmp(ext, ".gsave") == 0) return width <= 640 && height <= 400;
    if (b2026_strcasecmp(ext, ".tga") == 0 || b2026_strcasecmp(ext, ".gif") == 0 || b2026_strcasecmp(ext, ".jpg") == 0 || b2026_strcasecmp(ext, ".jpeg") == 0) limit = 65535;
    else if (b2026_strcasecmp(ext, ".psd") == 0) limit = 30000;
    else if (b2026_strcasecmp(ext, ".webp") == 0) limit = 16383;
    return width <= limit && height <= limit;
}

static unsigned int saveimage_crc32_update(unsigned int crc, const unsigned char *data, size_t length) {
    size_t i;
    int bit;
    crc = ~crc;
    for (i = 0; i < length; ++i) {
        crc ^= data[i];
        for (bit = 0; bit < 8; ++bit) {
            if (crc & 1U) crc = (crc >> 1) ^ 0xEDB88320U;
            else crc >>= 1;
        }
    }
    return ~crc;
}

static unsigned int saveimage_adler32(const unsigned char *data, size_t length) {
    unsigned int a = 1U;
    unsigned int b = 0U;
    size_t i;
    for (i = 0; i < length; ++i) {
        a = (a + data[i]) % 65521U;
        b = (b + a) % 65521U;
    }
    return (b << 16) | a;
}

static int g_saveimage_width = 0;
static int g_saveimage_height = 0;

static void saveimage_write_u16_le(FILE *fp, unsigned int value) {
    unsigned char bytes[2];
    bytes[0] = (unsigned char)(value & 255U);
    bytes[1] = (unsigned char)((value >> 8) & 255U);
    fwrite(bytes, 1, 2, fp);
}

static void saveimage_write_u16_be(FILE *fp, unsigned int value) {
    unsigned char bytes[2];
    bytes[0] = (unsigned char)((value >> 8) & 255U);
    bytes[1] = (unsigned char)(value & 255U);
    fwrite(bytes, 1, 2, fp);
}

static void saveimage_write_u32_le(FILE *fp, unsigned int value) {
    unsigned char bytes[4];
    bytes[0] = (unsigned char)(value & 255U);
    bytes[1] = (unsigned char)((value >> 8) & 255U);
    bytes[2] = (unsigned char)((value >> 16) & 255U);
    bytes[3] = (unsigned char)((value >> 24) & 255U);
    fwrite(bytes, 1, 4, fp);
}

static void saveimage_write_u32_be(FILE *fp, unsigned int value) {
    unsigned char bytes[4];
    bytes[0] = (unsigned char)((value >> 24) & 255U);
    bytes[1] = (unsigned char)((value >> 16) & 255U);
    bytes[2] = (unsigned char)((value >> 8) & 255U);
    bytes[3] = (unsigned char)(value & 255U);
    fwrite(bytes, 1, 4, fp);
}

static int saveimage_write_png_chunk(FILE *fp, const char *type, const unsigned char *data, unsigned int length) {
    unsigned int crc;
    saveimage_write_u32_be(fp, length);
    fwrite(type, 1, 4, fp);
    if (length > 0 && data != NULL) fwrite(data, 1, length, fp);
    crc = saveimage_crc32_update(0U, (const unsigned char *)type, 4);
    if (length > 0 && data != NULL) crc = saveimage_crc32_update(crc, data, length);
    saveimage_write_u32_be(fp, crc);
    return ferror(fp) ? 0 : 1;
}

static int saveimage_write_png(const char *path, const unsigned char *image_data, int width, int height, int bytes_per_sample, int phys_x, int phys_y, int phys_unit) {
    FILE *fp;
    unsigned char signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
    unsigned char ihdr[13];
    unsigned char phys[9];
    unsigned char *raw = NULL;
    unsigned char *zlib = NULL;
    size_t row_bytes;
    size_t raw_size;
    size_t zlib_capacity;
    size_t zlib_pos;
    size_t remaining;
    size_t offset;
    unsigned int adler;
    int y;
    int ok = 0;
    row_bytes = (size_t)width * 4U * (size_t)bytes_per_sample;
    raw_size = (row_bytes + 1U) * (size_t)height;
    raw = (unsigned char *)malloc(raw_size);
    if (raw == NULL) runtime_error("SAVEIMAGE failed: out of memory / 【画像保存】メモリ不足です。");
    for (y = 0; y < height; ++y) {
        size_t raw_row = (size_t)y * (row_bytes + 1U);
        size_t src_row = (size_t)y * row_bytes;
        raw[raw_row] = 0;
        memcpy(raw + raw_row + 1U, image_data + src_row, row_bytes);
    }
    zlib_capacity = 2U + raw_size + ((raw_size / 65535U) + 1U) * 5U + 4U;
    zlib = (unsigned char *)malloc(zlib_capacity);
    if (zlib == NULL) runtime_error("SAVEIMAGE failed: out of memory / 【画像保存】メモリ不足です。");
    zlib_pos = 0;
    zlib[zlib_pos++] = 0x78;
    zlib[zlib_pos++] = 0x01;
    remaining = raw_size;
    offset = 0;
    while (remaining > 0) {
        unsigned int block_len = (unsigned int)(remaining > 65535U ? 65535U : remaining);
        unsigned int final_flag = (remaining <= 65535U) ? 1U : 0U;
        zlib[zlib_pos++] = (unsigned char)final_flag;
        zlib[zlib_pos++] = (unsigned char)(block_len & 255U);
        zlib[zlib_pos++] = (unsigned char)((block_len >> 8) & 255U);
        zlib[zlib_pos++] = (unsigned char)((~block_len) & 255U);
        zlib[zlib_pos++] = (unsigned char)(((~block_len) >> 8) & 255U);
        memcpy(zlib + zlib_pos, raw + offset, block_len);
        zlib_pos += block_len;
        offset += block_len;
        remaining -= block_len;
    }
    adler = saveimage_adler32(raw, raw_size);
    zlib[zlib_pos++] = (unsigned char)((adler >> 24) & 255U);
    zlib[zlib_pos++] = (unsigned char)((adler >> 16) & 255U);
    zlib[zlib_pos++] = (unsigned char)((adler >> 8) & 255U);
    zlib[zlib_pos++] = (unsigned char)(adler & 255U);
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) {
        free(raw);
        free(zlib);
        return 0;
    }
    fwrite(signature, 1, 8, fp);
    ihdr[0] = (unsigned char)((width >> 24) & 255);
    ihdr[1] = (unsigned char)((width >> 16) & 255);
    ihdr[2] = (unsigned char)((width >> 8) & 255);
    ihdr[3] = (unsigned char)(width & 255);
    ihdr[4] = (unsigned char)((height >> 24) & 255);
    ihdr[5] = (unsigned char)((height >> 16) & 255);
    ihdr[6] = (unsigned char)((height >> 8) & 255);
    ihdr[7] = (unsigned char)(height & 255);
    ihdr[8] = (unsigned char)(bytes_per_sample == 2 ? 16 : 8);
    ihdr[9] = 6;
    ihdr[10] = 0;
    ihdr[11] = 0;
    ihdr[12] = 0;
    ok = saveimage_write_png_chunk(fp, "IHDR", ihdr, 13);
    if (ok && phys_x > 0 && phys_y > 0) {
        phys[0] = (unsigned char)((phys_x >> 24) & 255);
        phys[1] = (unsigned char)((phys_x >> 16) & 255);
        phys[2] = (unsigned char)((phys_x >> 8) & 255);
        phys[3] = (unsigned char)(phys_x & 255);
        phys[4] = (unsigned char)((phys_y >> 24) & 255);
        phys[5] = (unsigned char)((phys_y >> 16) & 255);
        phys[6] = (unsigned char)((phys_y >> 8) & 255);
        phys[7] = (unsigned char)(phys_y & 255);
        phys[8] = (unsigned char)(phys_unit != 0 ? 1 : 0);
        ok = saveimage_write_png_chunk(fp, "pHYs", phys, 9);
    }
    if (ok) ok = saveimage_write_png_chunk(fp, "IDAT", zlib, (unsigned int)zlib_pos);
    if (ok) ok = saveimage_write_png_chunk(fp, "IEND", NULL, 0);
    if (fclose(fp) != 0) ok = 0;
    free(raw);
    free(zlib);
    return ok;
}

static int saveimage_write_bmp32(const char *path, const unsigned char *rgba) {
    FILE *fp;
    unsigned int row_bytes = (unsigned int)(g_saveimage_width * 4);
    unsigned int data_size = row_bytes * (unsigned int)g_saveimage_height;
    unsigned int file_size = 54U + data_size;
    int x;
    int y;
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) return 0;
    fputc('B', fp);
    fputc('M', fp);
    saveimage_write_u32_le(fp, file_size);
    saveimage_write_u16_le(fp, 0);
    saveimage_write_u16_le(fp, 0);
    saveimage_write_u32_le(fp, 54U);
    saveimage_write_u32_le(fp, 40U);
    saveimage_write_u32_le(fp, (unsigned int)g_saveimage_width);
    saveimage_write_u32_le(fp, (unsigned int)g_saveimage_height);
    saveimage_write_u16_le(fp, 1U);
    saveimage_write_u16_le(fp, 32U);
    saveimage_write_u32_le(fp, 0U);
    saveimage_write_u32_le(fp, data_size);
    saveimage_write_u32_le(fp, 2835U);
    saveimage_write_u32_le(fp, 2835U);
    saveimage_write_u32_le(fp, 0U);
    saveimage_write_u32_le(fp, 0U);
    for (y = g_saveimage_height - 1; y >= 0; --y) {
        for (x = 0; x < g_saveimage_width; ++x) {
            size_t idx = ((size_t)y * (size_t)g_saveimage_width + (size_t)x) * 4U;
            fputc(rgba[idx + 2], fp);
            fputc(rgba[idx + 1], fp);
            fputc(rgba[idx + 0], fp);
            fputc(rgba[idx + 3], fp);
        }
    }
    if (ferror(fp)) {
        fclose(fp);
        return 0;
    }
    return fclose(fp) == 0;
}

static int saveimage_write_tga32(const char *path, const unsigned char *rgba) {
    if (g_saveimage_width <= 0 || g_saveimage_height <= 0 || g_saveimage_width > 65535 || g_saveimage_height > 65535) return 0;
    FILE *fp;
    int x;
    int y;
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) return 0;
    fputc(0, fp);
    fputc(0, fp);
    fputc(2, fp);
    saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 0U);
    fputc(0, fp);
    saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, (unsigned int)g_saveimage_width);
    saveimage_write_u16_le(fp, (unsigned int)g_saveimage_height);
    fputc(32, fp);
    fputc(0x28, fp);
    for (y = 0; y < g_saveimage_height; ++y) {
        for (x = 0; x < g_saveimage_width; ++x) {
            size_t idx = ((size_t)y * (size_t)g_saveimage_width + (size_t)x) * 4U;
            fputc(rgba[idx + 2], fp);
            fputc(rgba[idx + 1], fp);
            fputc(rgba[idx + 0], fp);
            fputc(rgba[idx + 3], fp);
        }
    }
    if (ferror(fp)) {
        fclose(fp);
        return 0;
    }
    return fclose(fp) == 0;
}

static unsigned char *saveimage_rgba_to_rgb(const unsigned char *rgba, int width, int height) {
    size_t pixel_count;
    size_t i;
    unsigned char *rgb;
    if (rgba == NULL || width <= 0 || height <= 0) return NULL;
    pixel_count = (size_t)width * (size_t)height;
    rgb = (unsigned char *)malloc(pixel_count * 3U);
    if (rgb == NULL) runtime_error("SAVEIMAGE failed: out of memory / 【画像保存】メモリ不足です。");
    for (i = 0U; i < pixel_count; ++i) {
        rgb[i * 3U + 0U] = rgba[i * 4U + 0U];
        rgb[i * 3U + 1U] = rgba[i * 4U + 1U];
        rgb[i * 3U + 2U] = rgba[i * 4U + 2U];
    }
    return rgb;
}

static unsigned char *saveimage_rgba_to_rgb_matte_white(const unsigned char *rgba, int width, int height) {
    size_t pixel_count;
    size_t i;
    unsigned char *rgb;
    if (rgba == NULL || width <= 0 || height <= 0) return NULL;
    pixel_count = (size_t)width * (size_t)height;
    rgb = (unsigned char *)malloc(pixel_count * 3U);
    if (rgb == NULL) runtime_error("SAVEIMAGE failed: out of memory / 【画像保存】メモリ不足です。");
    for (i = 0U; i < pixel_count; ++i) {
        unsigned int a = (unsigned int)rgba[i * 4U + 3U];
        unsigned int inv_a = 255U - a;
        rgb[i * 3U + 0U] = (unsigned char)(((unsigned int)rgba[i * 4U + 0U] * a + 255U * inv_a + 127U) / 255U);
        rgb[i * 3U + 1U] = (unsigned char)(((unsigned int)rgba[i * 4U + 1U] * a + 255U * inv_a + 127U) / 255U);
        rgb[i * 3U + 2U] = (unsigned char)(((unsigned int)rgba[i * 4U + 2U] * a + 255U * inv_a + 127U) / 255U);
    }
    return rgb;
}

static int saveimage_rgba_has_alpha(const unsigned char *rgba, int width, int height) {
    size_t pixel_count;
    size_t i;
    if (rgba == NULL || width <= 0 || height <= 0) return 0;
    pixel_count = (size_t)width * (size_t)height;
    for (i = 0U; i < pixel_count; ++i) {
        if (rgba[i * 4U + 3U] != 255U) return 1;
    }
    return 0;
}

static double saveimage_slot_number(const B2026_Var *slot);
static int saveimage_value_to_u8(double value, double source_value_max);
static unsigned int saveimage_value_to_u16(double value, double source_value_max);

static double saveimage_value_to_unit_float(double value, double source_value_max) {
    if (is_basic_null_internal(value) || isnan(value)) value = 0.0;
    if (!(source_value_max > 0.0) || !isfinite(source_value_max)) source_value_max = 1.0;
    value = value / source_value_max;
    if (value < 0.0) value = 0.0;
    if (value > 1.0) value = 1.0;
    return value;
}

static void saveimage_store_float32_be(unsigned char *dest, float value) {
    union { float f; unsigned char b[4]; } u;
    u.f = value;
    if (*(const unsigned char *)&(const unsigned short){1} != 0) {
        dest[0] = u.b[3];
        dest[1] = u.b[2];
        dest[2] = u.b[1];
        dest[3] = u.b[0];
    } else {
        dest[0] = u.b[0];
        dest[1] = u.b[1];
        dest[2] = u.b[2];
        dest[3] = u.b[3];
    }
}

static int saveimage_psd_output_depth(int pixel_mode) {
    if (pixel_mode == 2) return 16;
    if (pixel_mode == 0) return 32;
    return 8;
}

static int saveimage_write_psd_color_mode_data(FILE *fp, int pixel_mode) {
    static const unsigned char hdrt_data[68] = {
        0x68,0x64,0x72,0x74,0x00,0x00,0x00,0x03,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x02,
        0x00,0x00,0x00,0x05,0x52,0x1d,0x67,0x1f,0x8a,0x2d,0x5b,0x9a,0x00,0x00,0x00,0x02,
        0x00,0x02,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x01,0x01,0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00,0x41,0x80,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
        0x3f,0x80,0x00,0x00
    };
    if (fp == NULL) return 0;
    if (pixel_mode == 0) {
        saveimage_write_u32_be(fp, (unsigned int)sizeof(hdrt_data));
        return fwrite(hdrt_data, 1, sizeof(hdrt_data), fp) == sizeof(hdrt_data);
    }
    saveimage_write_u32_be(fp, 0U);
    return 1;
}

static double saveimage_psd_channel_source_value(const B2026_Var *data, long long base_index, size_t pixel_index, int source_channels, int channel, double source_value_max) {
    if (data == NULL) return 0.0;
    if (source_channels >= 4) return saveimage_slot_number(&data[base_index + (long long)(pixel_index * 4U) + (long long)channel]);
    if (channel == 3) return source_value_max;
    return saveimage_slot_number(&data[base_index + (long long)pixel_index]);
}

static int saveimage_psd_channel_count(const B2026_Var *data, long long base_index, int width, int height, int source_channels, double source_value_max, int pixel_mode) {
    size_t pixel_count;
    size_t i;
    if (pixel_mode == 0) return 3;
    if (source_channels < 4) return 3;
    pixel_count = (size_t)width * (size_t)height;
    for (i = 0U; i < pixel_count; ++i) {
        double alpha = saveimage_psd_channel_source_value(data, base_index, i, source_channels, 3, source_value_max);
        if (pixel_mode == 2) {
            if (saveimage_value_to_u16(alpha, source_value_max) != 65535U) return 4;
        } else if (pixel_mode == 0) {
            if (saveimage_value_to_unit_float(alpha, source_value_max) < 0.999999) return 4;
        } else {
            if (saveimage_value_to_u8(alpha, source_value_max) != 255) return 4;
        }
    }
    return 3;
}

static void saveimage_psd_store_sample(unsigned char *dest, double value, double source_value_max, int pixel_mode) {
    if (pixel_mode == 2) {
        unsigned int value16 = saveimage_value_to_u16(value, source_value_max);
        dest[0] = (unsigned char)((value16 >> 8) & 255U);
        dest[1] = (unsigned char)(value16 & 255U);
    } else if (pixel_mode == 0) {
        saveimage_store_float32_be(dest, (float)saveimage_value_to_unit_float(value, source_value_max));
    } else {
        dest[0] = (unsigned char)saveimage_value_to_u8(value, source_value_max);
    }
}

static int saveimage_write_psd_none_from_array(const char *path, const B2026_Var *data, long long base_index, int width, int height, int source_channels, double source_value_max, int pixel_mode) {
    FILE *fp;
    size_t pixel_count;
    size_t i;
    int channel;
    int channel_count;
    int depth;
    unsigned char sample[4];
    if (path == NULL || data == NULL || width <= 0 || height <= 0) return 0;
    channel_count = saveimage_psd_channel_count(data, base_index, width, height, source_channels, source_value_max, pixel_mode);
    depth = saveimage_psd_output_depth(pixel_mode);
    fp = fopen(path, "wb");
    if (fp == NULL) return 0;
    fwrite("8BPS", 1, 4, fp);
    saveimage_write_u16_be(fp, 1U);
    fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp);
    saveimage_write_u16_be(fp, (unsigned short)channel_count);
    saveimage_write_u32_be(fp, (unsigned int)height);
    saveimage_write_u32_be(fp, (unsigned int)width);
    saveimage_write_u16_be(fp, (unsigned short)depth);
    saveimage_write_u16_be(fp, 3U);
    if (!saveimage_write_psd_color_mode_data(fp, pixel_mode)) {
        fclose(fp);
        return 0;
    }
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u16_be(fp, 0U);
    pixel_count = (size_t)width * (size_t)height;
    for (channel = 0; channel < channel_count; ++channel) {
        for (i = 0U; i < pixel_count; ++i) {
            double value = saveimage_psd_channel_source_value(data, base_index, i, source_channels, channel, source_value_max);
            size_t sample_size;
            saveimage_psd_store_sample(sample, value, source_value_max, pixel_mode);
            sample_size = (depth == 32) ? 4U : (depth == 16 ? 2U : 1U);
            if (fwrite(sample, 1, sample_size, fp) != sample_size) {
                fclose(fp);
                return 0;
            }
        }
    }
    if (fclose(fp) != 0) return 0;
    return 1;
}

static int saveimage_write_psd_none(const char *path, const unsigned char *rgba, int width, int height) {
    FILE *fp;
    unsigned char *rgb;
    size_t pixel_count;
    size_t i;
    int channel;
    int channel_count;
    int has_alpha;
    if (path == NULL || rgba == NULL || width <= 0 || height <= 0) return 0;
    has_alpha = saveimage_rgba_has_alpha(rgba, width, height);
    channel_count = has_alpha ? 4 : 3;
    rgb = saveimage_rgba_to_rgb(rgba, width, height);
    if (rgb == NULL) return 0;
    fp = fopen(path, "wb");
    if (fp == NULL) {
        free(rgb);
        return 0;
    }
    fwrite("8BPS", 1, 4, fp);
    saveimage_write_u16_be(fp, 1U);
    fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp);
    saveimage_write_u16_be(fp, (unsigned short)channel_count);
    saveimage_write_u32_be(fp, (unsigned int)height);
    saveimage_write_u32_be(fp, (unsigned int)width);
    saveimage_write_u16_be(fp, 8U);
    saveimage_write_u16_be(fp, 3U);
    if (!saveimage_write_psd_color_mode_data(fp, 1)) {
        fclose(fp);
        free(rgb);
        return 0;
    }
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u16_be(fp, 0U);
    pixel_count = (size_t)width * (size_t)height;
    for (channel = 0; channel < channel_count; ++channel) {
        for (i = 0U; i < pixel_count; ++i) {
            unsigned char value = (channel < 3) ? rgb[i * 3U + (size_t)channel] : rgba[i * 4U + 3U];
            if (fputc((int)value, fp) == EOF) {
                fclose(fp);
                free(rgb);
                return 0;
            }
        }
    }
    if (fclose(fp) != 0) {
        free(rgb);
        return 0;
    }
    free(rgb);
    return 1;
}

static int saveimage_psd_packbits_encode_row(const unsigned char *src, size_t length, unsigned char **out_data, size_t *out_size) {
    unsigned char *buffer;
    size_t capacity;
    size_t out_pos;
    size_t i;
    if (out_data == NULL || out_size == NULL) return 0;
    *out_data = NULL;
    *out_size = 0U;
    if (src == NULL && length > 0U) return 0;
    capacity = length == 0U ? 1U : (length * 2U + 2U);
    buffer = (unsigned char *)malloc(capacity);
    if (buffer == NULL) return 0;
    out_pos = 0U;
    i = 0U;
    while (i < length) {
        size_t run = 1U;
        while (i + run < length && run < 128U && src[i + run] == src[i]) ++run;
        if (run >= 3U) {
            buffer[out_pos++] = (unsigned char)(257U - run);
            buffer[out_pos++] = src[i];
            i += run;
        } else {
            size_t literal_start = i;
            size_t literal_len = 0U;
            while (i < length && literal_len < 128U) {
                run = 1U;
                while (i + run < length && run < 128U && src[i + run] == src[i]) ++run;
                if (run >= 3U) break;
                ++i;
                ++literal_len;
            }
            buffer[out_pos++] = (unsigned char)(literal_len - 1U);
            memcpy(buffer + out_pos, src + literal_start, literal_len);
            out_pos += literal_len;
        }
    }
    *out_data = buffer;
    *out_size = out_pos;
    return 1;
}

static int saveimage_write_psd_rle_from_array(const char *path, const B2026_Var *data, long long base_index, int width, int height, int source_channels, double source_value_max, int pixel_mode) {
    FILE *fp;
    unsigned char *row_buf;
    unsigned char **encoded_rows;
    unsigned short *row_lengths;
    size_t row_count;
    size_t idx;
    int channel;
    int y;
    int channel_count;
    int depth;
    size_t bytes_per_sample;
    size_t row_bytes;
    if (path == NULL || data == NULL || width <= 0 || height <= 0) return 0;
    channel_count = saveimage_psd_channel_count(data, base_index, width, height, source_channels, source_value_max, pixel_mode);
    depth = saveimage_psd_output_depth(pixel_mode);
    bytes_per_sample = (depth == 32) ? 4U : (depth == 16 ? 2U : 1U);
    row_bytes = (size_t)width * bytes_per_sample;
    row_count = (size_t)height * (size_t)channel_count;
    row_buf = (unsigned char *)malloc(row_bytes == 0U ? 1U : row_bytes);
    encoded_rows = (unsigned char **)calloc(row_count, sizeof(unsigned char *));
    row_lengths = (unsigned short *)calloc(row_count, sizeof(unsigned short));
    if (row_buf == NULL || encoded_rows == NULL || row_lengths == NULL) {
        free(row_buf);
        free(encoded_rows);
        free(row_lengths);
        return 0;
    }
    idx = 0U;
    for (channel = 0; channel < channel_count; ++channel) {
        for (y = 0; y < height; ++y) {
            size_t x;
            unsigned char *encoded = NULL;
            size_t encoded_size = 0U;
            for (x = 0U; x < (size_t)width; ++x) {
                size_t pixel_index = ((size_t)y * (size_t)width + x);
                double value = saveimage_psd_channel_source_value(data, base_index, pixel_index, source_channels, channel, source_value_max);
                saveimage_psd_store_sample(row_buf + x * bytes_per_sample, value, source_value_max, pixel_mode);
            }
            if (!saveimage_psd_packbits_encode_row(row_buf, row_bytes, &encoded, &encoded_size) || encoded_size > 65535U) {
                size_t j;
                free(encoded);
                for (j = 0U; j < idx; ++j) free(encoded_rows[j]);
                free(row_buf);
                free(encoded_rows);
                free(row_lengths);
                return 0;
            }
            encoded_rows[idx] = encoded;
            row_lengths[idx] = (unsigned short)encoded_size;
            ++idx;
        }
    }
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) {
        size_t j;
        for (j = 0U; j < idx; ++j) free(encoded_rows[j]);
        free(row_buf);
        free(encoded_rows);
        free(row_lengths);
        return 0;
    }
    fwrite("8BPS", 1, 4, fp);
    saveimage_write_u16_be(fp, 1U);
    fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp);
    saveimage_write_u16_be(fp, (unsigned short)channel_count);
    saveimage_write_u32_be(fp, (unsigned int)height);
    saveimage_write_u32_be(fp, (unsigned int)width);
    saveimage_write_u16_be(fp, (unsigned short)depth);
    saveimage_write_u16_be(fp, 3U);
    if (!saveimage_write_psd_color_mode_data(fp, pixel_mode)) {
        size_t j;
        fclose(fp);
        for (j = 0U; j < row_count; ++j) free(encoded_rows[j]);
        free(row_buf);
        free(encoded_rows);
        free(row_lengths);
        return 0;
    }
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u16_be(fp, 1U);
    for (idx = 0U; idx < row_count; ++idx) saveimage_write_u16_be(fp, row_lengths[idx]);
    for (idx = 0U; idx < row_count; ++idx) {
        if (row_lengths[idx] > 0U && fwrite(encoded_rows[idx], 1, (size_t)row_lengths[idx], fp) != (size_t)row_lengths[idx]) {
            fclose(fp);
            for (idx = 0U; idx < row_count; ++idx) free(encoded_rows[idx]);
            free(row_buf);
            free(encoded_rows);
            free(row_lengths);
            return 0;
        }
    }
    int write_failed = ferror(fp);
    if (fclose(fp) != 0) write_failed = 1;
    if (write_failed) {
        for (idx = 0U; idx < row_count; ++idx) free(encoded_rows[idx]);
        free(row_buf);
        free(encoded_rows);
        free(row_lengths);
        return 0;
    }
    for (idx = 0U; idx < row_count; ++idx) free(encoded_rows[idx]);
    free(row_buf);
    free(encoded_rows);
    free(row_lengths);
    return 1;
}

static int saveimage_write_psd_rle(const char *path, const unsigned char *rgba, int width, int height) {
    FILE *fp;
    unsigned char *rgb;
    unsigned char *row_buf;
    unsigned char **encoded_rows;
    unsigned short *row_lengths;
    size_t pixel_count;
    size_t row_count;
    size_t idx;
    int channel;
    int y;
    int channel_count;
    int has_alpha;
    if (path == NULL || rgba == NULL || width <= 0 || height <= 0) return 0;
    has_alpha = saveimage_rgba_has_alpha(rgba, width, height);
    channel_count = has_alpha ? 4 : 3;
    rgb = saveimage_rgba_to_rgb(rgba, width, height);
    if (rgb == NULL) return 0;
    pixel_count = (size_t)width * (size_t)height;
    row_count = (size_t)height * (size_t)channel_count;
    row_buf = (unsigned char *)malloc((size_t)width);
    encoded_rows = (unsigned char **)calloc(row_count, sizeof(unsigned char *));
    row_lengths = (unsigned short *)calloc(row_count, sizeof(unsigned short));
    if (row_buf == NULL || encoded_rows == NULL || row_lengths == NULL) {
        free(row_buf);
        free(encoded_rows);
        free(row_lengths);
        free(rgb);
        return 0;
    }
    idx = 0U;
    for (channel = 0; channel < channel_count; ++channel) {
        for (y = 0; y < height; ++y) {
            size_t x;
            unsigned char *encoded = NULL;
            size_t encoded_size = 0U;
            for (x = 0U; x < (size_t)width; ++x) {
                size_t pixel_index = ((size_t)y * (size_t)width + x);
                row_buf[x] = (channel < 3) ? rgb[pixel_index * 3U + (size_t)channel] : rgba[pixel_index * 4U + 3U];
            }
            if (!saveimage_psd_packbits_encode_row(row_buf, (size_t)width, &encoded, &encoded_size) || encoded_size > 65535U) {
                size_t j;
                free(encoded);
                for (j = 0U; j < idx; ++j) free(encoded_rows[j]);
                free(row_buf);
                free(encoded_rows);
                free(row_lengths);
                free(rgb);
                return 0;
            }
            encoded_rows[idx] = encoded;
            row_lengths[idx] = (unsigned short)encoded_size;
            ++idx;
        }
    }
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) {
        size_t j;
        for (j = 0U; j < idx; ++j) free(encoded_rows[j]);
        free(row_buf);
        free(encoded_rows);
        free(row_lengths);
        free(rgb);
        return 0;
    }
    fwrite("8BPS", 1, 4, fp);
    saveimage_write_u16_be(fp, 1U);
    fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp); fputc(0, fp);
    saveimage_write_u16_be(fp, (unsigned short)channel_count);
    saveimage_write_u32_be(fp, (unsigned int)height);
    saveimage_write_u32_be(fp, (unsigned int)width);
    saveimage_write_u16_be(fp, 8U);
    saveimage_write_u16_be(fp, 3U);
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u32_be(fp, 0U);
    saveimage_write_u16_be(fp, 1U);
    for (idx = 0U; idx < row_count; ++idx) saveimage_write_u16_be(fp, row_lengths[idx]);
    for (idx = 0U; idx < row_count; ++idx) {
        if (row_lengths[idx] > 0U && fwrite(encoded_rows[idx], 1, (size_t)row_lengths[idx], fp) != (size_t)row_lengths[idx]) {
            fclose(fp);
            for (idx = 0U; idx < row_count; ++idx) free(encoded_rows[idx]);
            free(row_buf);
            free(encoded_rows);
            free(row_lengths);
            free(rgb);
            return 0;
        }
    }
    int write_failed = ferror(fp);
    if (fclose(fp) != 0) write_failed = 1;
    if (write_failed) {
        for (idx = 0U; idx < row_count; ++idx) free(encoded_rows[idx]);
        free(row_buf);
        free(encoded_rows);
        free(row_lengths);
        free(rgb);
        return 0;
    }
    for (idx = 0U; idx < row_count; ++idx) free(encoded_rows[idx]);
    free(row_buf);
    free(encoded_rows);
    free(row_lengths);
    free(rgb);
    (void)pixel_count;
    return 1;
}

static void saveimage_stbi_write_callback(void *context, void *data, int size) {
    FILE *fp = (FILE *)context;
    if (fp == NULL || data == NULL || size <= 0) return;
    fwrite(data, 1, (size_t)size, fp);
}

static int saveimage_normalize_jpeg_quality(double quality_value) {
    if (is_basic_null_internal(quality_value) || isnan(quality_value) || isinf(quality_value)) return 91;
    if (quality_value < 1.0 || quality_value > 100.0) return 91;
    return (int)(quality_value + 0.5);
}

static int saveimage_write_tiff_none(const char *path, const unsigned char *rgba, int width, int height) {
    FILE *fp;
    unsigned char *rgb;
    unsigned int ifd_offset = 8U;
    unsigned short entry_count = 10U;
    unsigned int ifd_size = 2U + (unsigned int)entry_count * 12U + 4U;
    unsigned int bits_offset = ifd_offset + ifd_size;
    unsigned int data_offset = bits_offset + 6U;
    unsigned int image_bytes;
    rgb = saveimage_rgba_to_rgb(rgba, width, height);
    if (rgb == NULL) return 0;
    image_bytes = (unsigned int)((size_t)width * (size_t)height * 3U);
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) {
        free(rgb);
        return 0;
    }
    fputc('I', fp);
    fputc('I', fp);
    saveimage_write_u16_le(fp, 42U);
    saveimage_write_u32_le(fp, ifd_offset);
    saveimage_write_u16_le(fp, entry_count);
    saveimage_write_u16_le(fp, 256U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, (unsigned int)width);
    saveimage_write_u16_le(fp, 257U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, (unsigned int)height);
    saveimage_write_u16_le(fp, 258U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 3U); saveimage_write_u32_le(fp, bits_offset);
    saveimage_write_u16_le(fp, 259U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 1U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 262U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 2U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 273U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, data_offset);
    saveimage_write_u16_le(fp, 277U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 3U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 278U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, (unsigned int)height);
    saveimage_write_u16_le(fp, 279U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, image_bytes);
    saveimage_write_u16_le(fp, 284U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 1U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u32_le(fp, 0U);
    saveimage_write_u16_le(fp, 8U); saveimage_write_u16_le(fp, 8U); saveimage_write_u16_le(fp, 8U);
    if (image_bytes > 0U && fwrite(rgb, 1U, image_bytes, fp) != image_bytes) {
        fclose(fp);
        free(rgb);
        return 0;
    }
    if (ferror(fp)) {
        fclose(fp);
        free(rgb);
        return 0;
    }
    if (fclose(fp) != 0) {
        free(rgb);
        return 0;
    }
    free(rgb);
    return 1;
}

static int saveimage_lzw_emit_code(unsigned char **data, size_t *size, size_t *capacity, size_t *bit_pos, int code, int code_size) {
    int bit_index;
    size_t required;
    if (data == NULL || size == NULL || capacity == NULL || bit_pos == NULL) return 0;
    required = (*bit_pos + (size_t)code_size + 7U) / 8U;
    if (required > *capacity) {
        size_t new_capacity = *capacity == 0U ? 256U : *capacity;
        unsigned char *new_data;
        while (new_capacity < required) new_capacity *= 2U;
        new_data = (unsigned char *)realloc(*data, new_capacity);
        if (new_data == NULL) return 0;
        memset(new_data + *capacity, 0, new_capacity - *capacity);
        *data = new_data;
        *capacity = new_capacity;
    }
    for (bit_index = code_size - 1; bit_index >= 0; --bit_index) {
        size_t byte_index = *bit_pos / 8U;
        int shift = 7 - (int)(*bit_pos % 8U);
        if (((unsigned int)code >> (unsigned int)bit_index) & 1U) (*data)[byte_index] = (unsigned char)((*data)[byte_index] | (unsigned char)(1U << (unsigned int)shift));
        *bit_pos += 1U;
    }
    if (required > *size) *size = required;
    return 1;
}

static int saveimage_lzw_find_code(const int *hash_prefix, const unsigned char *hash_suffix, const int *hash_code, int hash_size, int prefix, unsigned char suffix) {
    unsigned int start;
    int slot;
    if (hash_prefix == NULL || hash_suffix == NULL || hash_code == NULL || hash_size <= 0) return -1;
    start = ((unsigned int)prefix * 257U + (unsigned int)suffix) % (unsigned int)hash_size;
    for (slot = 0; slot < hash_size; ++slot) {
        int index = (int)((start + (unsigned int)slot) % (unsigned int)hash_size);
        if (hash_code[index] < 0) return -1;
        if (hash_prefix[index] == prefix && hash_suffix[index] == suffix) return hash_code[index];
    }
    return -1;
}

static int saveimage_lzw_add_code(int *hash_prefix, unsigned char *hash_suffix, int *hash_code, int hash_size, int prefix, unsigned char suffix, int code) {
    unsigned int start;
    int slot;
    if (hash_prefix == NULL || hash_suffix == NULL || hash_code == NULL || hash_size <= 0) return 0;
    start = ((unsigned int)prefix * 257U + (unsigned int)suffix) % (unsigned int)hash_size;
    for (slot = 0; slot < hash_size; ++slot) {
        int index = (int)((start + (unsigned int)slot) % (unsigned int)hash_size);
        if (hash_code[index] < 0) {
            hash_prefix[index] = prefix;
            hash_suffix[index] = suffix;
            hash_code[index] = code;
            return 1;
        }
    }
    return 0;
}

static unsigned char *saveimage_tiff_lzw_encode(const unsigned char *src, size_t src_size, size_t *out_size) {
    const int clear_code = 256;
    const int eoi_code = 257;
    const int hash_size = 8192;
    int *hash_prefix;
    unsigned char *hash_suffix;
    int *hash_code;
    unsigned char *out = NULL;
    size_t size = 0U;
    size_t capacity = 0U;
    size_t bit_pos = 0U;
    int next_code = 258;
    int code_size = 9;
    size_t i;
    int prefix;
    if (out_size != NULL) *out_size = 0U;
    hash_prefix = (int *)malloc(sizeof(int) * (size_t)hash_size);
    hash_suffix = (unsigned char *)malloc((size_t)hash_size);
    hash_code = (int *)malloc(sizeof(int) * (size_t)hash_size);
    if (hash_prefix == NULL || hash_suffix == NULL || hash_code == NULL) {
        free(hash_prefix); free(hash_suffix); free(hash_code);
        return NULL;
    }
    for (i = 0U; i < (size_t)hash_size; ++i) hash_code[i] = -1;
    if (!saveimage_lzw_emit_code(&out, &size, &capacity, &bit_pos, clear_code, code_size)) {
        free(hash_prefix); free(hash_suffix); free(hash_code); free(out);
        return NULL;
    }
    if (src != NULL && src_size > 0U) {
        prefix = (int)src[0];
        for (i = 1U; i < src_size; ++i) {
            int found = saveimage_lzw_find_code(hash_prefix, hash_suffix, hash_code, hash_size, prefix, src[i]);
            if (found >= 0) {
                prefix = found;
            } else {
                int limit;
                if (!saveimage_lzw_emit_code(&out, &size, &capacity, &bit_pos, prefix, code_size)) {
                    free(hash_prefix); free(hash_suffix); free(hash_code); free(out);
                    return NULL;
                }
                if (next_code <= 4095) {
                    if (!saveimage_lzw_add_code(hash_prefix, hash_suffix, hash_code, hash_size, prefix, src[i], next_code)) {
                        free(hash_prefix); free(hash_suffix); free(hash_code); free(out);
                        return NULL;
                    }
                    next_code += 1;
                    limit = 1 << code_size;
                    if (next_code >= limit && code_size < 12) code_size += 1;
                } else {
                    if (!saveimage_lzw_emit_code(&out, &size, &capacity, &bit_pos, clear_code, code_size)) {
                        free(hash_prefix); free(hash_suffix); free(hash_code); free(out);
                        return NULL;
                    }
                    { size_t reset_index; for (reset_index = 0U; reset_index < (size_t)hash_size; ++reset_index) hash_code[reset_index] = -1; }
                    next_code = 258;
                    code_size = 9;
                }
                prefix = (int)src[i];
            }
        }
        if (!saveimage_lzw_emit_code(&out, &size, &capacity, &bit_pos, prefix, code_size)) {
            free(hash_prefix); free(hash_suffix); free(hash_code); free(out);
            return NULL;
        }
    }
    if (!saveimage_lzw_emit_code(&out, &size, &capacity, &bit_pos, eoi_code, code_size)) {
        free(hash_prefix); free(hash_suffix); free(hash_code); free(out);
        return NULL;
    }
    free(hash_prefix); free(hash_suffix); free(hash_code);
    if (out_size != NULL) *out_size = size;
    return out;
}

static int saveimage_write_tiff_lzw(const char *path, const unsigned char *rgba, int width, int height) {
    FILE *fp;
    unsigned char *rgb;
    unsigned char *compressed;
    size_t compressed_size = 0U;
    unsigned int ifd_offset = 8U;
    unsigned short entry_count = 10U;
    unsigned int ifd_size = 2U + (unsigned int)entry_count * 12U + 4U;
    unsigned int bits_offset = ifd_offset + ifd_size;
    unsigned int data_offset = bits_offset + 6U;
    rgb = saveimage_rgba_to_rgb(rgba, width, height);
    if (rgb == NULL) return 0;
    compressed = saveimage_tiff_lzw_encode(rgb, (size_t)width * (size_t)height * 3U, &compressed_size);
    free(rgb);
    if (compressed == NULL) return 0;
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) {
        free(compressed);
        return 0;
    }
    fputc('I', fp);
    fputc('I', fp);
    saveimage_write_u16_le(fp, 42U);
    saveimage_write_u32_le(fp, ifd_offset);
    saveimage_write_u16_le(fp, entry_count);
    saveimage_write_u16_le(fp, 256U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, (unsigned int)width);
    saveimage_write_u16_le(fp, 257U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, (unsigned int)height);
    saveimage_write_u16_le(fp, 258U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 3U); saveimage_write_u32_le(fp, bits_offset);
    saveimage_write_u16_le(fp, 259U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 5U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 262U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 2U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 273U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, data_offset);
    saveimage_write_u16_le(fp, 277U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 3U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 278U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, (unsigned int)height);
    saveimage_write_u16_le(fp, 279U); saveimage_write_u16_le(fp, 4U); saveimage_write_u32_le(fp, 1U); saveimage_write_u32_le(fp, (unsigned int)compressed_size);
    saveimage_write_u16_le(fp, 284U); saveimage_write_u16_le(fp, 3U); saveimage_write_u32_le(fp, 1U); saveimage_write_u16_le(fp, 1U); saveimage_write_u16_le(fp, 0U);
    saveimage_write_u32_le(fp, 0U);
    saveimage_write_u16_le(fp, 8U); saveimage_write_u16_le(fp, 8U); saveimage_write_u16_le(fp, 8U);
    if (compressed_size > 0U && fwrite(compressed, 1U, compressed_size, fp) != compressed_size) {
        fclose(fp);
        free(compressed);
        return 0;
    }
    free(compressed);
    if (ferror(fp)) {
        fclose(fp);
        return 0;
    }
    return fclose(fp) == 0;
}

static int saveimage_write_jpeg24(const char *path, const unsigned char *rgba, int width, int height, int quality) {
    unsigned char *rgb = saveimage_rgba_to_rgb_matte_white(rgba, width, height);
    FILE *fp;
    int ok;
    if (rgb == NULL) return 0;
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) {
        free(rgb);
        return 0;
    }
    ok = stbi_write_jpg_to_func(saveimage_stbi_write_callback, fp, width, height, 3, rgb, quality);
    fclose(fp);
    free(rgb);
    return ok != 0;
}



#if defined(__APPLE__) && B2026_HAVE_MACOS_IMAGEIO
static void saveimage_cg_data_release_callback(void *info, const void *data, size_t size) {
    (void)info;
    (void)size;
    free((void *)data);
}
static int saveimage_cg_destination_supports_type(CFStringRef identifier) {
    CFArrayRef types = NULL;
    CFIndex count;
    CFIndex index;
    int found = 0;
    if (identifier == NULL) return 0;
    types = CGImageDestinationCopyTypeIdentifiers();
    if (types == NULL) return 0;
    count = CFArrayGetCount(types);
    for (index = 0; index < count; ++index) {
        const void *value = CFArrayGetValueAtIndex(types, index);
        if (value != NULL && CFGetTypeID(value) == CFStringGetTypeID() && CFStringCompare((CFStringRef)value, identifier, 0) == kCFCompareEqualTo) {
            found = 1;
            break;
        }
    }
    CFRelease(types);
    return found;
}
#endif

static int saveimage_write_avif(const char *path, const unsigned char *rgba, int width, int height, float quality) {
#if defined(__APPLE__) && B2026_HAVE_MACOS_IMAGEIO
    int result = B2026_AVIF_WRITE_WRITE_ERROR;
    size_t rgba_size;
    unsigned char *copy = NULL;
    CGDataProviderRef provider = NULL;
    CGColorSpaceRef color_space = NULL;
    CGImageRef image_ref = NULL;
    CFURLRef url_ref = NULL;
    CGImageDestinationRef dest_ref = NULL;
    CFMutableDictionaryRef options = NULL;
    CFNumberRef quality_ref = NULL;
    double quality_value;
    if (path == NULL || rgba == NULL || width <= 0 || height <= 0) return B2026_AVIF_WRITE_WRITE_ERROR;
    if (!saveimage_cg_destination_supports_type(CFSTR("public.avif"))) return B2026_AVIF_WRITE_CODEC_UNAVAILABLE;
    if (quality < 0.0f) quality = 0.0f;
    if (quality > 100.0f) quality = 100.0f;
    rgba_size = (size_t)width * (size_t)height * 4U;
    copy = (unsigned char *)malloc(rgba_size);
    if (copy == NULL) return B2026_AVIF_WRITE_OUT_OF_MEMORY;
    memcpy(copy, rgba, rgba_size);
    provider = CGDataProviderCreateWithData(NULL, copy, rgba_size, saveimage_cg_data_release_callback);
    if (provider == NULL) {
        free(copy);
        return B2026_AVIF_WRITE_OUT_OF_MEMORY;
    }
    color_space = CGColorSpaceCreateDeviceRGB();
    if (color_space == NULL) {
        result = B2026_AVIF_WRITE_OUT_OF_MEMORY;
        goto cleanup;
    }
    image_ref = CGImageCreate((size_t)width, (size_t)height, 8U, 32U, (size_t)width * 4U, color_space, kCGImageAlphaLast | kCGBitmapByteOrderDefault, provider, NULL, false, kCGRenderingIntentDefault);
    if (image_ref == NULL) goto cleanup;
    url_ref = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)path, (CFIndex)strlen(path), false);
    if (url_ref == NULL) {
        result = B2026_AVIF_WRITE_OUT_OF_MEMORY;
        goto cleanup;
    }
    dest_ref = CGImageDestinationCreateWithURL(url_ref, CFSTR("public.avif"), 1U, NULL);
    if (dest_ref == NULL) {
        result = B2026_AVIF_WRITE_CODEC_UNAVAILABLE;
        goto cleanup;
    }
    options = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    if (options == NULL) {
        result = B2026_AVIF_WRITE_OUT_OF_MEMORY;
        goto cleanup;
    }
    quality_value = (double)quality / 100.0;
    quality_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &quality_value);
    if (quality_ref == NULL) {
        result = B2026_AVIF_WRITE_OUT_OF_MEMORY;
        goto cleanup;
    }
    CFDictionarySetValue(options, kCGImageDestinationLossyCompressionQuality, quality_ref);
    CGImageDestinationAddImage(dest_ref, image_ref, options);
    if (!CGImageDestinationFinalize(dest_ref)) {
        result = B2026_AVIF_WRITE_CODEC_UNAVAILABLE;
        goto cleanup;
    }
    result = B2026_AVIF_WRITE_OK;
cleanup:
    if (quality_ref != NULL) CFRelease(quality_ref);
    if (options != NULL) CFRelease(options);
    if (dest_ref != NULL) CFRelease(dest_ref);
    if (url_ref != NULL) CFRelease(url_ref);
    if (image_ref != NULL) CGImageRelease(image_ref);
    if (color_space != NULL) CGColorSpaceRelease(color_space);
    if (provider != NULL) CGDataProviderRelease(provider);
    return result;
#else
    (void)path;
    (void)rgba;
    (void)width;
    (void)height;
    (void)quality;
    return B2026_AVIF_WRITE_UNSUPPORTED_OS;
#endif
}

static void saveimage_gif_fill_rgb332_palette(B2026_QuantColor *palette, int palette_count) {
    int i;
    for (i = 0; i < palette_count; ++i) {
        int r3 = i / 32;
        int g3 = (i % 32) / 4;
        int b2 = i % 4;
        palette[i].r = (double)((r3 * 255 + 3) / 7);
        palette[i].g = (double)((g3 * 255 + 3) / 7);
        palette[i].b = (double)((b2 * 255 + 1) / 3);
    }
}

static int saveimage_gif_nearest_palette_index_u8(const unsigned char *palette_rgb, int palette_count, int r, int g, int b) {
    int i;
    int best_index = 0;
    int best_distance;
    if (palette_count <= 0) return 0;
    {
        int dr = r - (int)palette_rgb[0];
        int dg = g - (int)palette_rgb[1];
        int db = b - (int)palette_rgb[2];
        best_distance = dr * dr + dg * dg + db * db;
    }
    for (i = 1; i < palette_count; ++i) {
        int base = i * 3;
        int dr = r - (int)palette_rgb[base + 0];
        int dg = g - (int)palette_rgb[base + 1];
        int db = b - (int)palette_rgb[base + 2];
        int dist = dr * dr + dg * dg + db * db;
        if (dist < best_distance) {
            best_distance = dist;
            best_index = i;
        }
    }
    return best_index;
}

static unsigned char *saveimage_gif_build_palette_lookup(const unsigned char *palette_rgb, int palette_count) {
    unsigned char *lookup = (unsigned char *)malloc(32768U);
    int r5;
    int g5;
    int b5;
    if (lookup == NULL) return NULL;
    for (r5 = 0; r5 < 32; ++r5) {
        int r = (r5 * 255 + 15) / 31;
        for (g5 = 0; g5 < 32; ++g5) {
            int g = (g5 * 255 + 15) / 31;
            for (b5 = 0; b5 < 32; ++b5) {
                int b = (b5 * 255 + 15) / 31;
                int idx = (r5 << 10) | (g5 << 5) | b5;
                lookup[idx] = (unsigned char)saveimage_gif_nearest_palette_index_u8(palette_rgb, palette_count, r, g, b);
            }
        }
    }
    return lookup;
}

static int saveimage_gif_lookup_palette_index(const unsigned char *lookup, const unsigned char *palette_rgb, int palette_count, int r, int g, int b) {
    if (lookup != NULL) return (int)lookup[((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3)];
    return saveimage_gif_nearest_palette_index_u8(palette_rgb, palette_count, r, g, b);
}

static int saveimage_gif_build_adaptive_palette(const unsigned char *rgba, int width, int height, int color_count, B2026_QuantColor *palette, int palette_max) {
    int pixel_count = width * height;
    int sample_limit = filter_quantize_sample_limit(pixel_count, color_count);
    int stride = 1;
    int sample_count = 0;
    int i;
    int iter;
    double *samples = NULL;
    if (color_count < 1) return 0;
    if (color_count > palette_max) color_count = palette_max;
    if (pixel_count <= 0) return 0;
    if (sample_limit < pixel_count) stride = (pixel_count + sample_limit - 1) / sample_limit;
    samples = (double *)malloc((size_t)sample_limit * 3U * sizeof(double));
    if (samples == NULL) return 0;
    for (i = 0; i < pixel_count && sample_count < sample_limit; i += stride) {
        int base = i * 4;
        if (rgba[base + 3] == 0U) continue;
        samples[sample_count * 3 + 0] = (double)rgba[base + 0];
        samples[sample_count * 3 + 1] = (double)rgba[base + 1];
        samples[sample_count * 3 + 2] = (double)rgba[base + 2];
        sample_count++;
    }
    if (sample_count <= 0) {
        free(samples);
        palette[0].r = 0.0;
        palette[0].g = 0.0;
        palette[0].b = 0.0;
        return 1;
    }
    if (sample_count < color_count) color_count = sample_count;
    for (i = 0; i < color_count; ++i) {
        int index = (i * sample_count) / color_count;
        palette[i].r = samples[index * 3 + 0];
        palette[i].g = samples[index * 3 + 1];
        palette[i].b = samples[index * 3 + 2];
    }
    for (iter = 0; iter < 6; ++iter) {
        double *sum_r = (double *)calloc((size_t)color_count, sizeof(double));
        double *sum_g = (double *)calloc((size_t)color_count, sizeof(double));
        double *sum_b = (double *)calloc((size_t)color_count, sizeof(double));
        int *counts = (int *)calloc((size_t)color_count, sizeof(int));
        if (sum_r == NULL || sum_g == NULL || sum_b == NULL || counts == NULL) {
            free(sum_r);
            free(sum_g);
            free(sum_b);
            free(counts);
            free(samples);
            return 0;
        }
        for (i = 0; i < sample_count; ++i) {
            double r = samples[i * 3 + 0];
            double g = samples[i * 3 + 1];
            double b = samples[i * 3 + 2];
            int nearest = filter_quantize_nearest_palette_index(palette, color_count, r, g, b);
            sum_r[nearest] += r;
            sum_g[nearest] += g;
            sum_b[nearest] += b;
            counts[nearest] += 1;
        }
        for (i = 0; i < color_count; ++i) {
            if (counts[i] > 0) {
                palette[i].r = sum_r[i] / (double)counts[i];
                palette[i].g = sum_g[i] / (double)counts[i];
                palette[i].b = sum_b[i] / (double)counts[i];
            }
        }
        free(sum_r);
        free(sum_g);
        free(sum_b);
        free(counts);
    }
    free(samples);
    return color_count;
}

static void saveimage_gif_apply_palette_none_indices(const unsigned char *rgba, int pixel_count, const unsigned char *palette_rgb, const unsigned char *palette_lookup, int palette_count, int has_transparent, int transparent_index, unsigned char *indices_out) {
    int i;
    for (i = 0; i < pixel_count; ++i) {
        int base = i * 4;
        if (has_transparent && rgba[base + 3] == 0U) indices_out[i] = (unsigned char)transparent_index;
        else indices_out[i] = (unsigned char)saveimage_gif_lookup_palette_index(palette_lookup, palette_rgb, palette_count, (int)rgba[base + 0], (int)rgba[base + 1], (int)rgba[base + 2]);
    }
}

static void saveimage_gif_apply_palette_ordered_indices(const unsigned char *rgba, int width, int height, const unsigned char *palette_rgb, const unsigned char *palette_lookup, int palette_count, int has_transparent, int transparent_index, unsigned char *indices_out) {
    static const int bayer4x4[16] = {
        0, 8, 2, 10,
        12, 4, 14, 6,
        3, 11, 1, 9,
        15, 7, 13, 5
    };
    double amount = 255.0 / (double)((palette_count > 1 ? palette_count - 1 : 1) * 2);
    int x;
    int y;
    int index_pos = 0;
    for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
            int base = index_pos * 4;
            if (has_transparent && rgba[base + 3] == 0U) {
                indices_out[index_pos++] = (unsigned char)transparent_index;
            } else {
                double d = (((double)bayer4x4[(y & 3) * 4 + (x & 3)] + 0.5) / 16.0) - 0.5;
                int r = (int)floor((double)rgba[base + 0] + d * amount + 0.5);
                int g = (int)floor((double)rgba[base + 1] + d * amount + 0.5);
                int b = (int)floor((double)rgba[base + 2] + d * amount + 0.5);
                if (r < 0) r = 0; else if (r > 255) r = 255;
                if (g < 0) g = 0; else if (g > 255) g = 255;
                if (b < 0) b = 0; else if (b > 255) b = 255;
                indices_out[index_pos++] = (unsigned char)saveimage_gif_lookup_palette_index(palette_lookup, palette_rgb, palette_count, r, g, b);
            }
        }
    }
}

static void saveimage_gif_apply_palette_fs_indices(const unsigned char *rgba, int width, int height, const unsigned char *palette_rgb, const unsigned char *palette_lookup, int palette_count, int has_transparent, int transparent_index, unsigned char *indices_out) {
    int pixel_count = width * height;
    float *work = (float *)malloc((size_t)pixel_count * 3U * sizeof(float));
    int x;
    int y;
    if (work == NULL) {
        saveimage_gif_apply_palette_none_indices(rgba, pixel_count, palette_rgb, palette_lookup, palette_count, has_transparent, transparent_index, indices_out);
        return;
    }
    for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
            int idx = y * width + x;
            int base = idx * 4;
            work[idx * 3 + 0] = (float)rgba[base + 0];
            work[idx * 3 + 1] = (float)rgba[base + 1];
            work[idx * 3 + 2] = (float)rgba[base + 2];
        }
    }
    for (y = 0; y < height; ++y) {
        for (x = 0; x < width; ++x) {
            int idx = y * width + x;
            int base = idx * 4;
            if (has_transparent && rgba[base + 3] == 0U) {
                indices_out[idx] = (unsigned char)transparent_index;
            } else {
                float old_r = work[idx * 3 + 0];
                float old_g = work[idx * 3 + 1];
                float old_b = work[idx * 3 + 2];
                int r = (int)floor((double)old_r + 0.5);
                int g = (int)floor((double)old_g + 0.5);
                int b = (int)floor((double)old_b + 0.5);
                int nearest;
                float new_r;
                float new_g;
                float new_b;
                float err_r;
                float err_g;
                float err_b;
                if (r < 0) r = 0; else if (r > 255) r = 255;
                if (g < 0) g = 0; else if (g > 255) g = 255;
                if (b < 0) b = 0; else if (b > 255) b = 255;
                nearest = saveimage_gif_lookup_palette_index(palette_lookup, palette_rgb, palette_count, r, g, b);
                indices_out[idx] = (unsigned char)nearest;
                new_r = (float)palette_rgb[nearest * 3 + 0];
                new_g = (float)palette_rgb[nearest * 3 + 1];
                new_b = (float)palette_rgb[nearest * 3 + 2];
                err_r = old_r - new_r;
                err_g = old_g - new_g;
                err_b = old_b - new_b;
                if (x + 1 < width) {
                    int n = idx + 1;
                    work[n * 3 + 0] += err_r * (7.0f / 16.0f);
                    work[n * 3 + 1] += err_g * (7.0f / 16.0f);
                    work[n * 3 + 2] += err_b * (7.0f / 16.0f);
                }
                if (y + 1 < height) {
                    if (x > 0) {
                        int n = idx + width - 1;
                        work[n * 3 + 0] += err_r * (3.0f / 16.0f);
                        work[n * 3 + 1] += err_g * (3.0f / 16.0f);
                        work[n * 3 + 2] += err_b * (3.0f / 16.0f);
                    }
                    {
                        int n = idx + width;
                        work[n * 3 + 0] += err_r * (5.0f / 16.0f);
                        work[n * 3 + 1] += err_g * (5.0f / 16.0f);
                        work[n * 3 + 2] += err_b * (5.0f / 16.0f);
                    }
                    if (x + 1 < width) {
                        int n = idx + width + 1;
                        work[n * 3 + 0] += err_r * (1.0f / 16.0f);
                        work[n * 3 + 1] += err_g * (1.0f / 16.0f);
                        work[n * 3 + 2] += err_b * (1.0f / 16.0f);
                    }
                }
            }
        }
    }
    free(work);
}

static int saveimage_gif_build_palette_and_indices(const unsigned char *rgba, int width, int height, int family_opt, int mode_kind, B2026_QuantColor *palette, unsigned char *palette_rgb, unsigned char **indices_out, int *has_transparent_out, int *transparent_index_out) {
    unsigned char *indices;
    unsigned char *palette_lookup = NULL;
    int pixel_count = width * height;
    int palette_count = 256;
    int has_transparent = 0;
    int transparent_index = -1;
    int i;
    for (i = 0; i < pixel_count; ++i) {
        if (rgba[i * 4 + 3] == 0U) {
            has_transparent = 1;
            break;
        }
    }
    if (has_transparent) {
        palette_count = 255;
        transparent_index = 255;
    }
    if (family_opt) {
        palette_count = saveimage_gif_build_adaptive_palette(rgba, width, height, palette_count, palette, palette_count);
        if (palette_count <= 0) return 0;
    } else {
        saveimage_gif_fill_rgb332_palette(palette, palette_count);
    }
    for (i = 0; i < 256; ++i) {
        int index = i < palette_count ? i : 0;
        palette_rgb[i * 3 + 0] = (unsigned char)fmax(0.0, fmin(255.0, floor(palette[index].r + 0.5)));
        palette_rgb[i * 3 + 1] = (unsigned char)fmax(0.0, fmin(255.0, floor(palette[index].g + 0.5)));
        palette_rgb[i * 3 + 2] = (unsigned char)fmax(0.0, fmin(255.0, floor(palette[index].b + 0.5)));
    }
    if (has_transparent) {
        palette_rgb[transparent_index * 3 + 0] = 0U;
        palette_rgb[transparent_index * 3 + 1] = 0U;
        palette_rgb[transparent_index * 3 + 2] = 0U;
    }
    palette_lookup = saveimage_gif_build_palette_lookup(palette_rgb, palette_count);
    indices = (unsigned char *)malloc((size_t)pixel_count);
    if (indices == NULL) { free(palette_lookup); return 0; }
    if (mode_kind == 2) saveimage_gif_apply_palette_fs_indices(rgba, width, height, palette_rgb, palette_lookup, palette_count, has_transparent, transparent_index, indices);
    else if (mode_kind == 1) saveimage_gif_apply_palette_ordered_indices(rgba, width, height, palette_rgb, palette_lookup, palette_count, has_transparent, transparent_index, indices);
    else saveimage_gif_apply_palette_none_indices(rgba, pixel_count, palette_rgb, palette_lookup, palette_count, has_transparent, transparent_index, indices);
    free(palette_lookup);
    *indices_out = indices;
    if (has_transparent_out != NULL) *has_transparent_out = has_transparent;
    if (transparent_index_out != NULL) *transparent_index_out = transparent_index;
    return 1;
}

static int saveimage_gif_parse_mode(const char *text, int *family_opt_out, int *mode_kind_out) {
    int family_opt = 1;
    int mode_kind = 2;
    if (text != NULL && *text != '\0') {
        if (b2026_strcasecmp(text, "RGB332") == 0 || strcmp(text, "固定256色") == 0) {
            family_opt = 0; mode_kind = 0;
        } else if (b2026_strcasecmp(text, "RGB332_DITHER") == 0 || b2026_strcasecmp(text, "RGB332_ORDERED") == 0 || strcmp(text, "固定256色ディザ") == 0 || strcmp(text, "固定256色ディザ階調") == 0) {
            family_opt = 0; mode_kind = 1;
        } else if (b2026_strcasecmp(text, "RGB332_FS") == 0 || strcmp(text, "固定256色誤差拡散") == 0 || strcmp(text, "固定256色誤差拡散法") == 0) {
            family_opt = 0; mode_kind = 2;
        } else if (b2026_strcasecmp(text, "OPT256") == 0 || strcmp(text, "最適256色") == 0) {
            family_opt = 1; mode_kind = 0;
        } else if (b2026_strcasecmp(text, "OPT256_DITHER") == 0 || b2026_strcasecmp(text, "OPT256_ORDERED") == 0 || strcmp(text, "最適256色ディザ") == 0 || strcmp(text, "最適256色ディザ階調") == 0) {
            family_opt = 1; mode_kind = 1;
        } else if (b2026_strcasecmp(text, "OPT256_FS") == 0 || strcmp(text, "最適256色誤差拡散") == 0 || strcmp(text, "最適256色誤差拡散法") == 0) {
            family_opt = 1; mode_kind = 2;
        } else {
            return 0;
        }
    }
    if (family_opt_out != NULL) *family_opt_out = family_opt;
    if (mode_kind_out != NULL) *mode_kind_out = mode_kind;
    return 1;
}

typedef struct SaveImageGifBitWriter {
    unsigned char *data;
    size_t size;
    size_t capacity;
    unsigned int bit_buffer;
    int bit_count;
} SaveImageGifBitWriter;

static void saveimage_gif_bit_writer_free(SaveImageGifBitWriter *writer) {
    if (writer == NULL) return;
    free(writer->data);
    writer->data = NULL;
    writer->size = 0U;
    writer->capacity = 0U;
    writer->bit_buffer = 0U;
    writer->bit_count = 0;
}

static int saveimage_gif_bit_writer_push_byte(SaveImageGifBitWriter *writer, unsigned char value) {
    unsigned char *new_data;
    size_t new_capacity;
    if (writer->size >= writer->capacity) {
        new_capacity = writer->capacity > 0U ? writer->capacity * 2U : 1024U;
        new_data = (unsigned char *)realloc(writer->data, new_capacity);
        if (new_data == NULL) return 0;
        writer->data = new_data;
        writer->capacity = new_capacity;
    }
    writer->data[writer->size++] = value;
    return 1;
}

static int saveimage_gif_bit_writer_write_code(SaveImageGifBitWriter *writer, int code, int code_size) {
    writer->bit_buffer |= ((unsigned int)code << writer->bit_count);
    writer->bit_count += code_size;
    while (writer->bit_count >= 8) {
        if (!saveimage_gif_bit_writer_push_byte(writer, (unsigned char)(writer->bit_buffer & 255U))) return 0;
        writer->bit_buffer >>= 8;
        writer->bit_count -= 8;
    }
    return 1;
}

static int saveimage_gif_bit_writer_flush(SaveImageGifBitWriter *writer) {
    if (writer->bit_count > 0) {
        if (!saveimage_gif_bit_writer_push_byte(writer, (unsigned char)(writer->bit_buffer & 255U))) return 0;
        writer->bit_buffer = 0U;
        writer->bit_count = 0;
    }
    return 1;
}

static int saveimage_write_gif(const char *path, const unsigned char *rgba, int width, int height, int family_opt, int mode_kind) {
    FILE *fp;
    B2026_QuantColor palette[256];
    unsigned char palette_rgb[256 * 3];
    unsigned char *indices = NULL;
    SaveImageGifBitWriter writer;
    int pixel_index;
    int has_transparent = 0;
    int transparent_index = 0;
    int code_size = 9;
    int clear_code = 256;
    int eoi_code = 257;
    memset(&writer, 0, sizeof(writer));
    if (path == NULL || rgba == NULL || width <= 0 || height <= 0) return 0;
    if (!saveimage_gif_build_palette_and_indices(rgba, width, height, family_opt, mode_kind, palette, palette_rgb, &indices, &has_transparent, &transparent_index)) {
        saveimage_gif_bit_writer_free(&writer);
        free(indices);
        return 0;
    }
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) {
        free(indices);
        return 0;
    }
    if (fwrite("GIF89a", 1U, 6U, fp) != 6U) {
        fclose(fp);
        free(indices);
        return 0;
    }
    saveimage_write_u16_le(fp, (unsigned int)width);
    saveimage_write_u16_le(fp, (unsigned int)height);
    if (ferror(fp) || fputc(0xF7, fp) == EOF || fputc(0, fp) == EOF || fputc(0, fp) == EOF) {
        fclose(fp);
        free(indices);
        return 0;
    }
    if (fwrite(palette_rgb, 1U, sizeof(palette_rgb), fp) != sizeof(palette_rgb)) {
        fclose(fp);
        free(indices);
        return 0;
    }
    if (has_transparent) {
        if (fputc(0x21, fp) == EOF || fputc(0xF9, fp) == EOF || fputc(0x04, fp) == EOF || fputc(0x01, fp) == EOF) {
            fclose(fp);
            free(indices);
            return 0;
        }
        saveimage_write_u16_le(fp, 0U);
        if (ferror(fp) || fputc(transparent_index, fp) == EOF || fputc(0, fp) == EOF) {
            fclose(fp);
            free(indices);
            return 0;
        }
    }
    if (fputc(0x2C, fp) == EOF) {
        fclose(fp);
        free(indices);
        return 0;
    }
    saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, 0U);
    saveimage_write_u16_le(fp, (unsigned int)width);
    saveimage_write_u16_le(fp, (unsigned int)height);
    if (ferror(fp) || fputc(0, fp) == EOF || fputc(8, fp) == EOF) {
        fclose(fp);
        free(indices);
        return 0;
    }
    for (pixel_index = 0; pixel_index < width * height; ++pixel_index) {
        int pix = (int)indices[pixel_index];
        if (!saveimage_gif_bit_writer_write_code(&writer, clear_code, code_size) ||
            !saveimage_gif_bit_writer_write_code(&writer, pix, code_size)) {
            fclose(fp);
            free(indices);
            saveimage_gif_bit_writer_free(&writer);
            return 0;
        }
    }
    if (!saveimage_gif_bit_writer_write_code(&writer, eoi_code, code_size) || !saveimage_gif_bit_writer_flush(&writer)) {
        fclose(fp);
        free(indices);
        saveimage_gif_bit_writer_free(&writer);
        return 0;
    }
    pixel_index = 0;
    while (pixel_index < (int)writer.size) {
        int chunk = (int)(writer.size - (size_t)pixel_index);
        if (chunk > 255) chunk = 255;
        if (fputc(chunk, fp) == EOF || fwrite(writer.data + pixel_index, 1U, (size_t)chunk, fp) != (size_t)chunk) {
            fclose(fp);
            free(indices);
            saveimage_gif_bit_writer_free(&writer);
            return 0;
        }
        pixel_index += chunk;
    }
    if (fputc(0, fp) == EOF || fputc(0x3B, fp) == EOF) {
        fclose(fp);
        free(indices);
        saveimage_gif_bit_writer_free(&writer);
        return 0;
    }
    free(indices);
    saveimage_gif_bit_writer_free(&writer);
    if (ferror(fp)) {
        fclose(fp);
        return 0;
    }
    return fclose(fp) == 0;
}
#if B2026_PROGRAM_USES_WEBP
#include <webp/decode.h>
#include <webp/demux.h>
#include <webp/encode.h>
static int saveimage_write_webp(const char *path, const unsigned char *rgba, int width, int height, int lossless, float quality, int alpha_quality) {
    WebPConfig config;
    WebPPicture picture;
    WebPMemoryWriter writer;
    FILE *fp = NULL;
    int ok = 0;
    if (rgba == NULL || width <= 0 || height <= 0) return 0;
    if (quality < 0.0f) quality = 0.0f;
    if (quality > 100.0f) quality = 100.0f;
    if (alpha_quality < 0) alpha_quality = 100;
    if (alpha_quality > 100) alpha_quality = 100;
    if (!WebPConfigInit(&config)) return 0;
    config.lossless = lossless ? 1 : 0;
    config.quality = quality;
    config.alpha_quality = alpha_quality;
    config.method = lossless ? 6 : 4;
    if (!WebPValidateConfig(&config)) return 0;
    if (!WebPPictureInit(&picture)) return 0;
    picture.use_argb = 1;
    picture.width = width;
    picture.height = height;
    WebPMemoryWriterInit(&writer);
    picture.writer = WebPMemoryWrite;
    picture.custom_ptr = &writer;
    if (!WebPPictureImportRGBA(&picture, rgba, width * 4)) goto cleanup;
    if (!WebPEncode(&config, &picture)) goto cleanup;
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) goto cleanup;
    if (writer.mem == NULL || writer.size == 0U) goto cleanup;
    if (fwrite(writer.mem, 1, writer.size, fp) != writer.size) goto cleanup;
    ok = 1;
cleanup:
    if (fp != NULL) fclose(fp);
    WebPPictureFree(&picture);
    WebPMemoryWriterClear(&writer);
    return ok;
}
#endif

static double saveimage_resolve_source_value_max(const ArrayVariable *array_var, int pixel_mode, int auto_scale_unit) {
    double source_value_max = array_var != NULL ? array_var->image_value_max : 0.0;
    if (pixel_mode == 0 && auto_scale_unit) return 1.0;
    if (source_value_max > 0.0 && isfinite(source_value_max)) return source_value_max;
    if (pixel_mode == 2) return 65535.0;
    if (pixel_mode == 0) return 1.0;
    return 255.0;
}

static int saveimage_value_to_u8(double value, double source_value_max) {
    if (is_basic_null_internal(value) || isnan(value)) value = 0.0;
    if (!(source_value_max > 0.0) || !isfinite(source_value_max)) source_value_max = 255.0;
    value = (value / source_value_max) * 255.0;
    if (value < 0.0) value = 0.0;
    if (value > 255.0) value = 255.0;
    return (int)(value + 0.5);
}

static unsigned int saveimage_value_to_u16(double value, double source_value_max) {
    if (is_basic_null_internal(value) || isnan(value)) value = 0.0;
    if (!(source_value_max > 0.0) || !isfinite(source_value_max)) source_value_max = 65535.0;
    value = (value / source_value_max) * 65535.0;
    if (value < 0.0) value = 0.0;
    if (value > 65535.0) value = 65535.0;
    return (unsigned int)(value + 0.5);
}

/*
 * PC-9801 MAG (MAKI02), 16-colour analogue mode.
 *
 * The first implementation deliberately emits only literal pixel groups:
 * flag A is all zero and flag B is empty.  This is larger than a compressed
 * MAG file, but it is deterministic and avoids decoder-dependent compressor
 * edge cases on old PC-9801 software.
 */
static unsigned int saveimage_mag_u16(const unsigned char *p) {
    return (unsigned int)p[0] | ((unsigned int)p[1] << 8);
}

static unsigned int saveimage_mag_u32(const unsigned char *p) {
    return (unsigned int)p[0] | ((unsigned int)p[1] << 8) |
           ((unsigned int)p[2] << 16) | ((unsigned int)p[3] << 24);
}

static void saveimage_mag_put_u16(unsigned char *p, unsigned int value) {
    p[0] = (unsigned char)(value & 255U);
    p[1] = (unsigned char)((value >> 8) & 255U);
}

static void saveimage_mag_put_u32(unsigned char *p, unsigned int value) {
    p[0] = (unsigned char)(value & 255U);
    p[1] = (unsigned char)((value >> 8) & 255U);
    p[2] = (unsigned char)((value >> 16) & 255U);
    p[3] = (unsigned char)((value >> 24) & 255U);
}

static int saveimage_mag_write_all(FILE *fp, const void *data, size_t length) {
    return length == 0U || fwrite(data, 1U, length, fp) == length;
}

static int saveimage_mag_verify(const char *path, int source_width, int height) {
    FILE *fp = NULL;
    unsigned char *file_data = NULL;
    long end_pos;
    size_t length;
    size_t terminator;
    size_t header_pos;
    size_t flag_a_size;
    size_t pixel_size;
    size_t i;
    unsigned int padded_width;
    unsigned int units;
    unsigned int off_a;
    unsigned int off_b;
    unsigned int off_p;
    int ok = 0;
    if (path == NULL || source_width <= 0 || height <= 0) return 0;
    padded_width = ((unsigned int)source_width + 7U) & ~7U;
    units = padded_width / 8U;
    flag_a_size = ((size_t)units * (size_t)height + 7U) / 8U;
    pixel_size = (size_t)padded_width * (size_t)height / 2U;
    fp = b2026_fopen(path, "rb");
    if (fp == NULL || fseek(fp, 0L, SEEK_END) != 0) goto cleanup;
    end_pos = ftell(fp);
    if (end_pos < 0L || fseek(fp, 0L, SEEK_SET) != 0) goto cleanup;
    length = (size_t)end_pos;
    if (length < 8U + 1U + 32U + 48U) goto cleanup;
    file_data = (unsigned char *)malloc(length);
    if (file_data == NULL) goto cleanup;
    if (fread(file_data, 1U, length, fp) != length) goto cleanup;
    if (memcmp(file_data, "MAKI02  ", 8U) != 0) goto cleanup;
    terminator = 8U;
    while (terminator < length && file_data[terminator] != 0x1AU) ++terminator;
    if (terminator >= length || terminator - 8U > 255U) goto cleanup;
    header_pos = terminator + 1U;
    if (header_pos > length || length - header_pos < 32U) goto cleanup;
    if (file_data[header_pos + 0U] != 0U || file_data[header_pos + 1U] != 0U ||
        file_data[header_pos + 2U] != 0U || file_data[header_pos + 3U] != 0U) goto cleanup;
    if (saveimage_mag_u16(file_data + header_pos + 4U) != 0U ||
        saveimage_mag_u16(file_data + header_pos + 6U) != 0U ||
        saveimage_mag_u16(file_data + header_pos + 8U) + 1U != padded_width ||
        saveimage_mag_u16(file_data + header_pos + 10U) + 1U != (unsigned int)height) goto cleanup;
    off_a = saveimage_mag_u32(file_data + header_pos + 12U);
    off_b = saveimage_mag_u32(file_data + header_pos + 16U);
    off_p = saveimage_mag_u32(file_data + header_pos + 24U);
    if (off_a != 80U || off_b != off_a + (unsigned int)flag_a_size || off_p != off_b) goto cleanup;
    if ((size_t)off_p > SIZE_MAX - header_pos || pixel_size > SIZE_MAX - header_pos - (size_t)off_p) goto cleanup;
    if (header_pos + (size_t)off_p + pixel_size != length) goto cleanup;
    for (i = 0U; i < flag_a_size; ++i) {
        if (file_data[header_pos + (size_t)off_a + i] != 0U) goto cleanup;
    }
    /* Decode every packed pixel pair.  Nibbles outside 0..15 are impossible,
       but walking the complete stream verifies that the decoded pixel count
       exactly matches the padded MAKI02 rectangle. */
    for (i = 0U; i < pixel_size; ++i) {
        unsigned char packed = file_data[header_pos + (size_t)off_p + i];
        unsigned int left = (unsigned int)(packed >> 4);
        unsigned int right = (unsigned int)(packed & 15U);
        if (left >= 16U || right >= 16U) goto cleanup;
    }
    ok = 1;
cleanup:
    if (fp != NULL) fclose(fp);
    free(file_data);
    return ok;
}

static int saveimage_mag_build_palette(const unsigned char *rgba, int width, int height, B2026_QuantColor *palette, int palette_max) {
    int pixel_count = width * height;
    int sample_limit = filter_quantize_sample_limit(pixel_count, palette_max);
    int stride = 1;
    int sample_count = 0;
    int color_count;
    int i;
    int iter;
    double *samples;
    if (rgba == NULL || pixel_count <= 0 || palette_max <= 0) return 0;
    if (sample_limit < pixel_count) stride = (pixel_count + sample_limit - 1) / sample_limit;
    samples = (double *)malloc((size_t)sample_limit * 3U * sizeof(double));
    if (samples == NULL) return -1;
    for (i = 0; i < pixel_count && sample_count < sample_limit; i += stride) {
        int base = i * 4;
        int r = (int)rgba[base + 0];
        int g = (int)rgba[base + 1];
        int b = (int)rgba[base + 2];
        if (r == 0 && g == 0 && b == 0) continue;
        samples[sample_count * 3 + 0] = (double)r;
        samples[sample_count * 3 + 1] = (double)g;
        samples[sample_count * 3 + 2] = (double)b;
        ++sample_count;
    }
    if (sample_count == 0) {
        free(samples);
        return 0;
    }
    palette[0].r = samples[0];
    palette[0].g = samples[1];
    palette[0].b = samples[2];
    color_count = 1;
    while (color_count < palette_max) {
        double farthest_distance = -1.0;
        int farthest_index = -1;
        for (i = 0; i < sample_count; ++i) {
            int nearest = filter_quantize_nearest_palette_index(
                palette, color_count,
                samples[i * 3 + 0], samples[i * 3 + 1], samples[i * 3 + 2]);
            double dr = samples[i * 3 + 0] - palette[nearest].r;
            double dg = samples[i * 3 + 1] - palette[nearest].g;
            double db = samples[i * 3 + 2] - palette[nearest].b;
            double distance = dr * dr + dg * dg + db * db;
            if (distance > farthest_distance) {
                farthest_distance = distance;
                farthest_index = i;
            }
        }
        if (farthest_index < 0 || farthest_distance <= 0.0) break;
        palette[color_count].r = samples[farthest_index * 3 + 0];
        palette[color_count].g = samples[farthest_index * 3 + 1];
        palette[color_count].b = samples[farthest_index * 3 + 2];
        ++color_count;
    }
    for (iter = 0; iter < 8; ++iter) {
        double sum_r[15];
        double sum_g[15];
        double sum_b[15];
        int counts[15];
        memset(sum_r, 0, sizeof(sum_r));
        memset(sum_g, 0, sizeof(sum_g));
        memset(sum_b, 0, sizeof(sum_b));
        memset(counts, 0, sizeof(counts));
        for (i = 0; i < sample_count; ++i) {
            int nearest = filter_quantize_nearest_palette_index(
                palette, color_count,
                samples[i * 3 + 0], samples[i * 3 + 1], samples[i * 3 + 2]);
            sum_r[nearest] += samples[i * 3 + 0];
            sum_g[nearest] += samples[i * 3 + 1];
            sum_b[nearest] += samples[i * 3 + 2];
            counts[nearest] += 1;
        }
        for (i = 0; i < color_count; ++i) {
            if (counts[i] > 0) {
                palette[i].r = sum_r[i] / (double)counts[i];
                palette[i].g = sum_g[i] / (double)counts[i];
                palette[i].b = sum_b[i] / (double)counts[i];
            }
        }
    }
    free(samples);
    return color_count;
}

static int saveimage_write_mag16(const char *path, const unsigned char *rgba, int width, int height) {
    static const unsigned char signature[8] = {'M','A','K','I','0','2',' ',' '};
    static const char comment[] = "BASIC2026 SAVEIMAGE";
    unsigned char header[32];
    unsigned char palette_rgb[16 * 3];
    B2026_QuantColor palette[16];
    unsigned char *opaque = NULL;
    unsigned char *indices = NULL;
    unsigned char *lookup = NULL;
    FILE *fp = NULL;
    size_t source_pixels;
    size_t padded_pixels;
    size_t flag_a_size;
    size_t y;
    int padded_width;
    int palette_colors;
    int palette_count;
    int ok = 0;
    unsigned int off_a;
    unsigned int off_b;
    unsigned int off_p;
    unsigned char zeros[4096];
    if (path == NULL || rgba == NULL || width <= 0 || height <= 0) return 0;
    padded_width = (width + 7) & ~7;
    if (padded_width <= 0 || padded_width > 65528 || height > 65535) return 0;
    source_pixels = (size_t)width * (size_t)height;
    padded_pixels = (size_t)padded_width * (size_t)height;
    if (source_pixels > SIZE_MAX / 4U) return 0;
    opaque = (unsigned char *)malloc(source_pixels * 4U);
    indices = (unsigned char *)calloc(padded_pixels, 1U);
    if (opaque == NULL || indices == NULL) goto cleanup;
    for (y = 0U; y < source_pixels; ++y) {
        unsigned int alpha = (unsigned int)rgba[y * 4U + 3U];
        opaque[y * 4U + 0U] = (unsigned char)(((unsigned int)rgba[y * 4U + 0U] * alpha + 127U) / 255U);
        opaque[y * 4U + 1U] = (unsigned char)(((unsigned int)rgba[y * 4U + 1U] * alpha + 127U) / 255U);
        opaque[y * 4U + 2U] = (unsigned char)(((unsigned int)rgba[y * 4U + 2U] * alpha + 127U) / 255U);
        opaque[y * 4U + 3U] = 255U;
    }
    memset(palette, 0, sizeof(palette));
    memset(palette_rgb, 0, sizeof(palette_rgb));
    /* Reserve palette entry 0 for the black right-hand padding. */
    palette_colors = saveimage_mag_build_palette(opaque, width, height, palette + 1, 15);
    if (palette_colors < 0) goto cleanup;
    palette_count = palette_colors + 1;
    for (y = 1U; y < (size_t)palette_count; ++y) {
        palette_rgb[y * 3U + 0U] = (unsigned char)fmax(0.0, fmin(255.0, floor(palette[y].r + 0.5)));
        palette_rgb[y * 3U + 1U] = (unsigned char)fmax(0.0, fmin(255.0, floor(palette[y].g + 0.5)));
        palette_rgb[y * 3U + 2U] = (unsigned char)fmax(0.0, fmin(255.0, floor(palette[y].b + 0.5)));
    }
    lookup = saveimage_gif_build_palette_lookup(palette_rgb, palette_count);
    if (lookup == NULL) goto cleanup;
    for (y = 0U; y < (size_t)height; ++y) {
        size_t x;
        for (x = 0U; x < (size_t)width; ++x) {
            size_t source = (y * (size_t)width + x) * 4U;
            indices[y * (size_t)padded_width + x] = (unsigned char)saveimage_gif_lookup_palette_index(
                lookup, palette_rgb, palette_count,
                (int)opaque[source + 0U], (int)opaque[source + 1U], (int)opaque[source + 2U]);
        }
    }
    flag_a_size = (((size_t)padded_width / 8U) * (size_t)height + 7U) / 8U;
    if (flag_a_size > (size_t)UINT_MAX - 80U) goto cleanup;
    off_a = 32U + 48U;
    off_b = off_a + (unsigned int)flag_a_size;
    off_p = off_b;
    memset(header, 0, sizeof(header));
    header[3] = 0U;
    saveimage_mag_put_u16(header + 4, 0U);
    saveimage_mag_put_u16(header + 6, 0U);
    saveimage_mag_put_u16(header + 8, (unsigned int)padded_width - 1U);
    saveimage_mag_put_u16(header + 10, (unsigned int)height - 1U);
    saveimage_mag_put_u32(header + 12, off_a);
    saveimage_mag_put_u32(header + 16, off_b);
    saveimage_mag_put_u32(header + 20, 0U);
    saveimage_mag_put_u32(header + 24, off_p);
    fp = b2026_fopen(path, "wb");
    if (fp == NULL) goto cleanup;
    if (!saveimage_mag_write_all(fp, signature, sizeof(signature)) ||
        !saveimage_mag_write_all(fp, comment, sizeof(comment) - 1U) || fputc(0x1A, fp) == EOF ||
        !saveimage_mag_write_all(fp, header, sizeof(header))) goto cleanup;
    /* MAG palettes are stored in G, R, B byte order. */
    for (y = 0U; y < 16U; ++y) {
        unsigned char grb[3];
        grb[0] = palette_rgb[y * 3U + 1U];
        grb[1] = palette_rgb[y * 3U + 0U];
        grb[2] = palette_rgb[y * 3U + 2U];
        if (!saveimage_mag_write_all(fp, grb, sizeof(grb))) goto cleanup;
    }
    memset(zeros, 0, sizeof(zeros));
    {
        size_t remaining = flag_a_size;
        while (remaining > 0U) {
            size_t chunk = remaining < sizeof(zeros) ? remaining : sizeof(zeros);
            if (!saveimage_mag_write_all(fp, zeros, chunk)) goto cleanup;
            remaining -= chunk;
        }
    }
    for (y = 0U; y < padded_pixels; y += 2U) {
        unsigned char packed = (unsigned char)((indices[y] << 4) | indices[y + 1U]);
        if (fputc((int)packed, fp) == EOF) goto cleanup;
    }
    if (fflush(fp) != 0 || ferror(fp) || fclose(fp) != 0) {
        fp = NULL;
        goto cleanup;
    }
    fp = NULL;
    ok = saveimage_mag_verify(path, width, height);
cleanup:
    if (fp != NULL) fclose(fp);
    free(lookup);
    free(indices);
    free(opaque);
    if (!ok && path != NULL) b2026_unlink(path);
    return ok;
}

/*
 * SHARP MZ-2500 BASIC-M25 GSAVE-compatible graphic data.
 *
 * In 16-colour mode the 4-bit colour number is I:G:R:B.  Each group of
 * eight pixels is stored as four bytes in B, R, G, I plane order, and bit 0
 * is the leftmost pixel.
 *
 * The 320x200 256-colour mode combines two RGBI screens.  The on-disk plane
 * order follows the two screens: B0,R0,G0,I0,B1,R1,G1,I1.  BASIC-M25's
 * normal 256-colour setup uses I0 as the red LSB, I1 as the green LSB and a
 * fixed zero as the blue LSB, giving an RGB 3:3:2 cube from the eight VRAM
 * planes.
 */
static void saveimage_mzg_palette_rgb(int index, int *r, int *g, int *b) {
    int intensity = (index & 8) != 0;
    int low = intensity ? 85 : 0;
    int high = intensity ? 255 : 170;
    *b = (index & 1) != 0 ? high : low;
    *r = (index & 2) != 0 ? high : low;
    *g = (index & 4) != 0 ? high : low;
}

static int saveimage_mzg_nearest(int r, int g, int b) {
    unsigned long best_distance = ULONG_MAX;
    int best = 0;
    int index;
    for (index = 0; index < 16; ++index) {
        int pr, pg, pb;
        long dr, dg, db;
        unsigned long distance;
        saveimage_mzg_palette_rgb(index, &pr, &pg, &pb);
        dr = (long)r - (long)pr;
        dg = (long)g - (long)pg;
        db = (long)b - (long)pb;
        distance = (unsigned long)(30L * dr * dr + 59L * dg * dg + 11L * db * db);
        if (distance < best_distance) {
            best_distance = distance;
            best = index;
        }
    }
    return best;
}

static void saveimage_mzg256_palette_rgb(int index, int *r, int *g, int *b) {
    int r3 = (((index & 0x20) != 0) ? 4 : 0) |
             (((index & 0x02) != 0) ? 2 : 0) |
             (((index & 0x80) != 0) ? 1 : 0);
    int g3 = (((index & 0x40) != 0) ? 4 : 0) |
             (((index & 0x04) != 0) ? 2 : 0) |
             (((index & 0x08) != 0) ? 1 : 0);
    int b2 = (((index & 0x10) != 0) ? 2 : 0) |
             (((index & 0x01) != 0) ? 1 : 0);
    *r = (r3 * 255 + 3) / 7;
    *g = (g3 * 255 + 3) / 7;
    *b = (b2 * 255 + 1) / 3;
}

static int saveimage_mzg256_nearest(int r, int g, int b) {
    int r3 = (r * 7 + 127) / 255;
    int g3 = (g * 7 + 127) / 255;
    int b2 = (b * 3 + 127) / 255;
    int index = 0;
    if ((r3 & 4) != 0) index |= 0x20;
    if ((r3 & 2) != 0) index |= 0x02;
    if ((r3 & 1) != 0) index |= 0x80;
    if ((g3 & 4) != 0) index |= 0x40;
    if ((g3 & 2) != 0) index |= 0x04;
    if ((g3 & 1) != 0) index |= 0x08;
    if ((b2 & 2) != 0) index |= 0x10;
    if ((b2 & 1) != 0) index |= 0x01;
    return index;
}

static void saveimage_mzg_palette_rgb_for_count(int color_count, int index, int *r, int *g, int *b) {
    if (color_count == 256) saveimage_mzg256_palette_rgb(index, r, g, b);
    else saveimage_mzg_palette_rgb(index, r, g, b);
}

static int saveimage_mzg_nearest_for_count(int color_count, int r, int g, int b) {
    return color_count == 256 ? saveimage_mzg256_nearest(r, g, b) : saveimage_mzg_nearest(r, g, b);
}

static int saveimage_mzg_clamp(int value) {
    if (value < 0) return 0;
    if (value > 255) return 255;
    return value;
}

static void saveimage_mzg_opaque_rgb(const unsigned char *rgba, size_t pixel, int *r, int *g, int *b) {
    unsigned int alpha = (unsigned int)rgba[pixel * 4U + 3U];
    *r = (int)(((unsigned int)rgba[pixel * 4U + 0U] * alpha + 127U) / 255U);
    *g = (int)(((unsigned int)rgba[pixel * 4U + 1U] * alpha + 127U) / 255U);
    *b = (int)(((unsigned int)rgba[pixel * 4U + 2U] * alpha + 127U) / 255U);
}

static int saveimage_mzg_parse_mode(const char *mode, int *mode_kind) {
    if (mode_kind == NULL) return 0;
    if (mode == NULL || mode[0] == '\0' || b2026_strcasecmp(mode, "FS") == 0 ||
        b2026_strcasecmp(mode, "FLOYDSTEINBERG") == 0 || strcmp(mode, "誤差拡散") == 0 ||
        strcmp(mode, "誤差拡散法") == 0) {
        *mode_kind = 2;
        return 1;
    }
    if (b2026_strcasecmp(mode, "ORDERED") == 0 || b2026_strcasecmp(mode, "DITHER") == 0 ||
        b2026_strcasecmp(mode, "BAYER") == 0 || strcmp(mode, "ディザ") == 0 ||
        strcmp(mode, "ディザ階調") == 0) {
        *mode_kind = 1;
        return 1;
    }
    if (b2026_strcasecmp(mode, "NONE") == 0 || b2026_strcasecmp(mode, "NEAREST") == 0 ||
        strcmp(mode, "ディザなし") == 0) {
        *mode_kind = 0;
        return 1;
    }
    return 0;
}

static int saveimage_mzg_parse_options(const char *option, int *color_count, int *mode_kind) {
    if (color_count == NULL || mode_kind == NULL) return 0;
    if (option != NULL && (b2026_strcasecmp(option, "16COLOR") == 0 ||
                           b2026_strcasecmp(option, "16COLORS") == 0 ||
                           strcmp(option, "16色") == 0)) {
        *color_count = 16;
        *mode_kind = 2;
        return 1;
    }
    if (option != NULL && (b2026_strcasecmp(option, "256COLOR") == 0 ||
                           b2026_strcasecmp(option, "256COLORS") == 0 ||
                           strcmp(option, "256色") == 0)) {
        *color_count = 256;
        *mode_kind = 2;
        return 1;
    }
    return saveimage_mzg_parse_mode(option, mode_kind);
}

static unsigned char *saveimage_mzg_quantize(const unsigned char *rgba, int width, int height, int color_count, int mode_kind) {
    static const int bayer4[16] = {
        0, 8, 2, 10,
        12, 4, 14, 6,
        3, 11, 1, 9,
        15, 7, 13, 5
    };
    size_t pixels;
    unsigned char *indices;
    int x, y;
    if (rgba == NULL || width <= 0 || height <= 0) return NULL;
    pixels = (size_t)width * (size_t)height;
    indices = (unsigned char *)malloc(pixels);
    if (indices == NULL) return NULL;
    if (mode_kind != 2) {
        for (y = 0; y < height; ++y) {
            for (x = 0; x < width; ++x) {
                size_t pixel = (size_t)y * (size_t)width + (size_t)x;
                int r, g, b;
                saveimage_mzg_opaque_rgb(rgba, pixel, &r, &g, &b);
                if (mode_kind == 1) {
                    int scale = color_count == 256 ? 2 : 4;
                    int delta = (bayer4[(y & 3) * 4 + (x & 3)] * 2 - 15) * scale;
                    r = saveimage_mzg_clamp(r + delta);
                    g = saveimage_mzg_clamp(g + delta);
                    b = saveimage_mzg_clamp(b + delta);
                }
                indices[pixel] = (unsigned char)saveimage_mzg_nearest_for_count(color_count, r, g, b);
            }
        }
        return indices;
    }
    {
        size_t row_values = ((size_t)width + 2U) * 3U;
        double *current = (double *)calloc(row_values, sizeof(double));
        double *next = (double *)calloc(row_values, sizeof(double));
        if (current == NULL || next == NULL) {
            free(current);
            free(next);
            free(indices);
            return NULL;
        }
        for (y = 0; y < height; ++y) {
            for (x = 0; x < width; ++x) {
                size_t pixel = (size_t)y * (size_t)width + (size_t)x;
                size_t pos = ((size_t)x + 1U) * 3U;
                int r0, g0, b0, r, g, b, pr, pg, pb, index;
                double er, eg, eb;
                saveimage_mzg_opaque_rgb(rgba, pixel, &r0, &g0, &b0);
                r = saveimage_mzg_clamp((int)floor((double)r0 + current[pos + 0U] + 0.5));
                g = saveimage_mzg_clamp((int)floor((double)g0 + current[pos + 1U] + 0.5));
                b = saveimage_mzg_clamp((int)floor((double)b0 + current[pos + 2U] + 0.5));
                index = saveimage_mzg_nearest_for_count(color_count, r, g, b);
                indices[pixel] = (unsigned char)index;
                saveimage_mzg_palette_rgb_for_count(color_count, index, &pr, &pg, &pb);
                er = (double)(r - pr);
                eg = (double)(g - pg);
                eb = (double)(b - pb);
                current[pos + 3U] += er * (7.0 / 16.0);
                current[pos + 4U] += eg * (7.0 / 16.0);
                current[pos + 5U] += eb * (7.0 / 16.0);
                next[pos - 3U] += er * (3.0 / 16.0);
                next[pos - 2U] += eg * (3.0 / 16.0);
                next[pos - 1U] += eb * (3.0 / 16.0);
                next[pos + 0U] += er * (5.0 / 16.0);
                next[pos + 1U] += eg * (5.0 / 16.0);
                next[pos + 2U] += eb * (5.0 / 16.0);
                next[pos + 3U] += er * (1.0 / 16.0);
                next[pos + 4U] += eg * (1.0 / 16.0);
                next[pos + 5U] += eb * (1.0 / 16.0);
            }
            {
                double *swap = current;
                current = next;
                next = swap;
                memset(next, 0, row_values * sizeof(double));
            }
        }
        free(current);
        free(next);
    }
    return indices;
}

static int saveimage_mzg_verify(const char *path, int width, int height, int color_count) {
    static const unsigned char signature[16] = {
        'g','r','a','p','h','i','c',' ','d','a','t','a',' ','0','0','0'
    };
    FILE *fp = NULL;
    unsigned char header[256];
    unsigned char planes[8];
    long file_size;
    size_t groups;
    size_t expected;
    int xgroup, y;
    int ok = 0;
    int plane_count = color_count == 256 ? 8 : 4;
    if (path == NULL || (color_count != 16 && color_count != 256) ||
        width <= 0 || width > 640 || height <= 0 || height > 400) return 0;
    if (color_count == 256 && (width != 320 || height != 200)) return 0;
    groups = ((size_t)width + 7U) / 8U;
    expected = 256U + groups * (size_t)height * (size_t)plane_count;
    fp = b2026_fopen(path, "rb");
    if (fp == NULL || fseek(fp, 0L, SEEK_END) != 0) goto cleanup;
    file_size = ftell(fp);
    if (file_size < 0L || (size_t)file_size != expected || fseek(fp, 0L, SEEK_SET) != 0) goto cleanup;
    if (fread(header, 1U, sizeof(header), fp) != sizeof(header)) goto cleanup;
    if (memcmp(header, signature, sizeof(signature)) != 0) goto cleanup;
    if (header[0x10] != 0U || header[0x11] != 0U || header[0x12] != 0U || header[0x13] != 0U) goto cleanup;
    if (((unsigned int)header[0x14] | ((unsigned int)header[0x15] << 8)) != (unsigned int)width) goto cleanup;
    if (((unsigned int)header[0x16] | ((unsigned int)header[0x17] << 8)) != (unsigned int)height) goto cleanup;
    if (header[0x18] != (color_count == 256 ? 0x20U : 0x10U)) goto cleanup;
    {
        size_t pos;
        for (pos = 0x19U; pos < sizeof(header); ++pos) if (header[pos] != 0U) goto cleanup;
    }
    for (y = 0; y < height; ++y) {
        for (xgroup = 0; xgroup < (int)groups; ++xgroup) {
            if (fread(planes, 1U, (size_t)plane_count, fp) != (size_t)plane_count) goto cleanup;
            if (xgroup == (int)groups - 1 && (width & 7) != 0) {
                unsigned int used_mask = (1U << (unsigned int)(width & 7)) - 1U;
                unsigned int combined = 0U;
                int plane;
                for (plane = 0; plane < plane_count; ++plane) combined |= (unsigned int)planes[plane];
                if ((combined & ~used_mask) != 0U) goto cleanup;
            }
        }
    }
    ok = fgetc(fp) == EOF && !ferror(fp);
cleanup:
    if (fp != NULL) fclose(fp);
    return ok;
}

static int saveimage_write_mzg(const char *path, const unsigned char *rgba, int width, int height, int color_count, int mode_kind) {
    static const unsigned char signature[16] = {
        'g','r','a','p','h','i','c',' ','d','a','t','a',' ','0','0','0'
    };
    unsigned char header[256];
    unsigned char *indices = NULL;
    FILE *fp = NULL;
    int y, group;
    int ok = 0;
    int plane_count = color_count == 256 ? 8 : 4;
    if (path == NULL || rgba == NULL || (color_count != 16 && color_count != 256) ||
        width <= 0 || width > 640 || height <= 0 || height > 400) return 0;
    if (color_count == 256 && (width != 320 || height != 200)) return 0;
    indices = saveimage_mzg_quantize(rgba, width, height, color_count, mode_kind);
    if (indices == NULL) goto cleanup;
    memset(header, 0, sizeof(header));
    memcpy(header, signature, sizeof(signature));
    header[0x14] = (unsigned char)((unsigned int)width & 255U);
    header[0x15] = (unsigned char)(((unsigned int)width >> 8) & 255U);
    header[0x16] = (unsigned char)((unsigned int)height & 255U);
    header[0x17] = (unsigned char)(((unsigned int)height >> 8) & 255U);
    header[0x18] = color_count == 256 ? 0x20U : 0x10U;
    fp = b2026_fopen(path, "wb");
    if (fp == NULL || fwrite(header, 1U, sizeof(header), fp) != sizeof(header)) goto cleanup;
    for (y = 0; y < height; ++y) {
        int groups = (width + 7) / 8;
        for (group = 0; group < groups; ++group) {
            unsigned char planes[8] = {0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U};
            int bit;
            for (bit = 0; bit < 8; ++bit) {
                int x = group * 8 + bit;
                if (x < width) {
                    unsigned int color = (unsigned int)indices[(size_t)y * (size_t)width + (size_t)x];
                    unsigned char mask = (unsigned char)(1U << (unsigned int)bit);
                    if (color_count == 256) {
                        if ((color & 16U) != 0U) planes[0] |= mask;
                        if ((color & 32U) != 0U) planes[1] |= mask;
                        if ((color & 64U) != 0U) planes[2] |= mask;
                        if ((color & 128U) != 0U) planes[3] |= mask;
                        if ((color & 1U) != 0U) planes[4] |= mask;
                        if ((color & 2U) != 0U) planes[5] |= mask;
                        if ((color & 4U) != 0U) planes[6] |= mask;
                        if ((color & 8U) != 0U) planes[7] |= mask;
                    } else {
                        if ((color & 1U) != 0U) planes[0] |= mask;
                        if ((color & 2U) != 0U) planes[1] |= mask;
                        if ((color & 4U) != 0U) planes[2] |= mask;
                        if ((color & 8U) != 0U) planes[3] |= mask;
                    }
                }
            }
            if (fwrite(planes, 1U, (size_t)plane_count, fp) != (size_t)plane_count) goto cleanup;
        }
    }
    if (fflush(fp) != 0 || ferror(fp) || fclose(fp) != 0) {
        fp = NULL;
        goto cleanup;
    }
    fp = NULL;
    ok = saveimage_mzg_verify(path, width, height, color_count);
cleanup:
    if (fp != NULL) fclose(fp);
    free(indices);
    if (!ok && path != NULL) b2026_unlink(path);
    return ok;
}
