From a056419c24ee86f6a672613353383e9bf18f72f4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jul 2026 16:23:45 +0300 Subject: [PATCH] kra_saver_fixed --- .kilo/plans/1784862272150-kra-format-fix.md | 163 +++++++++++++++++++ hcie-kra/src/kra_saver.rs | 168 +++++++++++++++----- hcie-kra/src/lib.rs | 41 ++++- hcie_features.ods | Bin 0 -> 18320 bytes 4 files changed, 329 insertions(+), 43 deletions(-) create mode 100644 .kilo/plans/1784862272150-kra-format-fix.md create mode 100644 hcie_features.ods diff --git a/.kilo/plans/1784862272150-kra-format-fix.md b/.kilo/plans/1784862272150-kra-format-fix.md new file mode 100644 index 0000000..b938066 --- /dev/null +++ b/.kilo/plans/1784862272150-kra-format-fix.md @@ -0,0 +1,163 @@ +# KRA Format Fix Plan + +## Root Causes (confirmed by analysis) + +### 1. VERSION Header Mismatch — Krita Crash + Self-Import Failures +**File:** `hcie-kra/src/kra_saver.rs` lines 324–329, 409–415 + +The saver writes `VERSION 2\n` in the VERS tile header, but the compressed payload uses +version byte `1` (meaning **no** delta decoding). Krita's VERS parser sees VERSION 2 and +applies delta decoding after decompression, producing garbage or a crash. + +Our own importer (`hcie-kra/src/lib.rs` line 158) also checks `if version >= 2` and +would apply delta decoding, producing wrong pixel data. + +**Fix:** Change both `encode_layer_vers` and `encode_mask_vers` to write `VERSION 1` +instead of `VERSION 2`. + +### 2. Fake "LZF" Compression — Bloated Files + Non-Standard +**File:** `hcie-kra/src/kra_saver.rs` lines 308–316, 394–403 + +The "LZF" compression is a custom RLE scheme that adds 513 bytes of overhead per tile +(1 version byte + 512 control bytes), making files **larger** than uncompressed. A full +1920×1080 layer (510 tiles) goes from 8.29 MB to 8.62 MB (+4%). The format happens to +look like real LZF literal runs, so our `lzf_decompress` function processes it correctly, +but a real LZF decoder would fail on tiles requiring back-references. + +**Fix:** Store tiles as RAW (uncompressed). Remove the fake compression logic and write +raw planar pixel data. Label tiles as `RAW` instead of `LZF` in the tile header. The +importer already handles RAW tiles correctly (lib.rs line 166–168). + +### 3. Invalid UUID Format — Krita Rejects Our XML +**File:** `hcie-kra/src/kra_saver.rs` line 240 + +UUIDs are generated from a single `u64` (8 bytes) and formatted as 32 hex digits with +leading zeros: `{00000000000000003aeba3f7cf423ad9}`. Proper UUIDs are 128 bits (16 bytes) +with dashed formatting: `{3aeba3f7-cf42-3ad9-…}`. Krita may validate UUID format and +reject our non-standard forms. + +**Fix:** Generate proper 128-bit random UUIDs using two `rand::random::()` calls +and format with dashes per standard UUID layout. + +### 4. Color Space / Bit Depth Ignored — Wrong Colors on Import +**File:** `hcie-kra/src/lib.rs` lines 647–698 + +The importer reads `colorspacename`, `channeldepth`, and `profile` attributes from the +`` and `` tags but ignores them. If the source file uses a non-RGBA color +space (e.g., CMYK, Grayscale) or 16-bit channel depth, the raw pixel bytes are +misinterpreted, producing wrong colors. + +**Fix:** At minimum, validate on import that: +- `colorspacename` is `"RGBA"` +- `channeldepth` is `"U8"` or `"U16"` (for U16, convert to U8 via shifting) +- Return an error if the format is unsupported + +### 5. Silent Layer Failures — Data Loss Without Notice +**File:** `hcie-kra/src/lib.rs` lines 790, 808, 814, 837 + +When a layer file can't be found, VERS parsing fails, or image decoding fails, the +layer is silently skipped or turned into a transparent layer. The user has no indication +that data was lost. + +**Fix:** Add `log::warn!` calls at each failure point, including the layer name and +reason for failure. + +### 6. Missing `layers.xml` Fallback +**File:** `hcie-kra/src/lib.rs` line 626 + +The main importer only tries `maindoc.xml`. Some Krita variants write `layers.xml` +instead. The v2 loader (`custom_loaders_kra_v2.rs`) has this fallback but it's separate. + +**Fix:** Add `layers.xml` as a secondary attempt in `try_import_krita_maindoc`. + +### 7. Inconsistent `DATA` Line Format +**File:** `hcie-kra/src/kra_saver.rs` lines 329 vs 415 + +`encode_layer_vers` writes `DATA {tiles.len()}\n` but `encode_mask_vers` writes `DATA\n` +(no count). The parser handles both, but it's inconsistent. + +**Fix:** Make both use `DATA\n` (no count) — the parser ignores the count anyway, and +removing it avoids an extra format requirement. + +--- + +## Implementation Tasks + +### Task A: Fix VERS header and compression (`kra_saver.rs`) +1. Change `writeln!(output, "VERSION 2")` → `writeln!(output, "VERSION 1")` in `encode_layer_vers` +2. Change `b"VERSION 2\n"` → `b"VERSION 1\n"` in `encode_mask_vers` +3. Remove the fake LZF compression loop in `encode_layer_vers` (lines 308–316) +4. Replace with writing raw planar pixel data directly (no version byte, no control bytes) +5. Update tile header tag from `"LZF"` to `"RAW"` (line 333) +6. Repeat steps 3–5 for `encode_mask_vers` (lines 394–403) + +### Task B: Fix UUID format (`kra_saver.rs`) +7. Replace `let uuid = format!("{{{:032x}}}", layer.id)` with proper 128-bit UUID: + ```rust + let high = rand::random::(); + let low = rand::random::(); + let uuid = format!( + "{{{:08x}-{:04x}-{:04x}-{:04x}-{:012x}}}", + (high >> 32) as u32, + (high >> 16) as u16 & 0xFFFF, + high as u16, + (low >> 48) as u16, + low & 0x0000_FFFF_FFFF_FFFF + ); + ``` +8. Apply same fix to mask UUID generation on line 257 + +### Task C: Add color space validation (`lib.rs`) +9. In `try_import_krita_maindoc`, after reading width/height from ``: + - Read `colorspacename` and `channeldepth` attributes + - If `colorspacename != "RGBA"`, return error: "Unsupported color space: {name}" + - If `channeldepth != "U8"`, return error: "Unsupported channel depth: {depth}" + - (Future: add U16→U8 conversion) + +### Task D: Add error logging (`lib.rs`) +10. Add `log::warn!` before each `continue` / silent fallback: + - Line 790 (layer file not found): warn with layer name + - Line 808 (SVG rasterization failed): warn with layer name + - Line 814 (VERS parse failed): warn with layer name + - Line 837 (image load failed): warn with layer name + +### Task E: Add `layers.xml` fallback (`lib.rs`) +11. In `try_import_krita_maindoc`, after the `maindoc.xml` attempt fails, try + `archive.by_name("layers.xml")` before returning `Ok(vec![])`. + +### Task F: Normalize `DATA` line format (`kra_saver.rs`) +12. Change line 329: `writeln!(output, "DATA {}", tiles.len())` → `writeln!(output, "DATA")` +13. The mask encoder already uses `b"DATA\n"` — leave unchanged. + +--- + +## Files to Modify + +| File | Changes | +|------|---------| +| `hcie-kra/src/kra_saver.rs` | Tasks A, B, F | +| `hcie-kra/src/lib.rs` | Tasks C, D, E | + +--- + +## Validation + +After implementing, verify with: + +1. **Roundtrip test:** Save a multi-layer document as KRA, then import it back. Compare + pixels and layer names against the original. + +2. **Krita compatibility:** Open the exported KRA in Krita ≥ 5.x. Verify it opens without + crash and displays correct colors. Also open the PSD→KRA exported file and verify it + matches the mergedimage.png preview. + +3. **Existing test suite:** Run `cargo test -p hcie-kra` and + `cargo test -p hcie-engine-api --test visual_regression` to ensure no regressions. + +4. **Edge cases:** + - Empty layers (all-transparent pixels) + - Layers with masks + - Group layers + - Layer names containing special characters (`&`, `<`, `>`, Unicode) + - Error on unsupported color space (e.g., CMYK, Grayscale) + - Error on unsupported bit depth (e.g., U16, F16) diff --git a/hcie-kra/src/kra_saver.rs b/hcie-kra/src/kra_saver.rs index c1cf48e..ff95002 100644 --- a/hcie-kra/src/kra_saver.rs +++ b/hcie-kra/src/kra_saver.rs @@ -1,5 +1,6 @@ use hcie_protocol::{Layer, LayerData, LayerType, VectorShape}; use hcie_protocol::BlendMode; +use rand; use std::io::{Cursor, Write}; use std::path::Path; @@ -231,13 +232,22 @@ fn generate_maindoc(layers: &[Layer], w: u32, h: u32, name: &str) -> String { let node_type = match layer.layer_type { LayerType::Raster | LayerType::Mask => "paintlayer", LayerType::Vector | LayerType::Text => "shapelayer", - LayerType::Group => "paintlayer", + LayerType::Group => "grouplayer", }; let visible = if layer.visible { "1" } else { "0" }; let opacity = (layer.opacity * 255.0) as u8; let filename = format!("layer{}", i + 1); - let uuid = format!("{{{:032x}}}", layer.id); + let uuid_high = rand::random::(); + let uuid_low = rand::random::(); + let uuid = format!( + "{{{:08x}-{:04x}-{:04x}-{:04x}-{:012x}}}", + (uuid_high >> 32) as u32, + (uuid_high >> 16) as u16 & 0xFFFF, + uuid_high as u16, + (uuid_low >> 48) as u16, + uuid_low & 0x0000_FFFF_FFFF_FFFF + ); let compositeop = blend_mode_to_krita_compositeop(layer.blend_mode); let escaped_name = layer.name @@ -254,9 +264,18 @@ fn generate_maindoc(layers: &[Layer], w: u32, h: u32, name: &str) -> String { if layer.mask_pixels.is_some() { xml.push_str(" \n"); let mask_filename = format!("{}_mask", filename); - let mask_uuid = format!("{{{:032x}}}", layer.id.wrapping_add(1)); // pseudo uuid + let mask_high = rand::random::(); + let mask_low = rand::random::(); + let mask_uuid = format!( + "{{{:08x}-{:04x}-{:04x}-{:04x}-{:012x}}}", + (mask_high >> 32) as u32, + (mask_high >> 16) as u16 & 0xFFFF, + mask_high as u16, + (mask_low >> 48) as u16, + mask_low & 0x0000_FFFF_FFFF_FFFF + ); xml.push_str(&format!( - " \n", + " \n", mask_filename, mask_uuid )); xml.push_str(" \n"); @@ -293,10 +312,11 @@ fn encode_layer_vers(layer: &Layer) -> Vec { let idx = (ly as usize * layer.width as usize + lx as usize) * 4; let tidx = y as usize * tw as usize + x as usize; - tile_raw[tidx] = layer.pixels[idx + 2]; - tile_raw[tidx + 1 * plane_size] = layer.pixels[idx + 1]; - tile_raw[tidx + 2 * plane_size] = layer.pixels[idx]; - tile_raw[tidx + 3 * plane_size] = layer.pixels[idx + 3]; + // Krita planar order: B, G, R, A + tile_raw[tidx] = layer.pixels[idx + 2]; // B + tile_raw[tidx + 1 * plane_size] = layer.pixels[idx + 1]; // G + tile_raw[tidx + 2 * plane_size] = layer.pixels[idx]; // R + tile_raw[tidx + 3 * plane_size] = layer.pixels[idx + 3]; // A if layer.pixels[idx + 3] > 0 { has_content = true; @@ -306,15 +326,14 @@ fn encode_layer_vers(layer: &Layer) -> Vec { } if has_content { - let mut compressed = vec![1u8]; - let mut i = 0; - while i < tile_raw.len() { - let chunk = (tile_raw.len() - i).min(32); - compressed.push((chunk - 1) as u8); - compressed.extend_from_slice(&tile_raw[i..i + chunk]); - i += chunk; - } - tiles.push((tx, ty, compressed)); + // Krita VERSION 2 tile format: + // payload = [version_byte = 1][lzf_compressed_planar_data] + // version_byte < 2 → no delta-decode on read (straightforward LZF). + let compressed = lzf_compress(&tile_raw); + let mut payload = Vec::with_capacity(1 + compressed.len()); + payload.push(1u8); // LZF version byte + payload.extend_from_slice(&compressed); + tiles.push((tx, ty, payload)); } tx += tw; } @@ -329,15 +348,93 @@ fn encode_layer_vers(layer: &Layer) -> Vec { writeln!(output, "DATA {}", tiles.len()).unwrap(); tiles.sort_by_key(|(x, y, _)| (*y, *x)); - for (tx, ty, tile_data) in tiles { - let header = format!("{},{},LZF,{}\n", tx, ty, tile_data.len()); + for (tx, ty, payload) in tiles { + let header = format!("{},{},LZF,{}\n", tx, ty, payload.len()); output.extend_from_slice(header.as_bytes()); - output.extend_from_slice(&tile_data); + output.extend_from_slice(&payload); } output } +/// Purpose: LZF compress a byte slice for use as Krita tile payload. +/// Logic: Simple LZF implementation — hash-table based back-reference search. +/// Each control byte describes either a literal run (ctrl < 32) or a +/// back-reference (ctrl >= 32). This matches the format expected by the +/// lzf_decompress reader in lib.rs. +fn lzf_compress(input: &[u8]) -> Vec { + // Worst-case output: all literals → 1 control byte per 32 data bytes. + let mut output = Vec::with_capacity(input.len() + input.len() / 20 + 32); + let mut hash_table: Vec = vec![usize::MAX; 1 << 16]; + + let mut ip = 0usize; + let mut lit_start = 0usize; + + // Flush pending literal bytes [lit_start .. lit_end) into output. + let flush_literals = |out: &mut Vec, data: &[u8], from: usize, to: usize| { + let mut i = from; + while i < to { + let run = (to - i).min(32); + out.push((run - 1) as u8); // ctrl: literal run length − 1 + out.extend_from_slice(&data[i..i + run]); + i += run; + } + }; + + while ip + 2 < input.len() { + // 16-bit hash of three bytes at ip. + let h = ((input[ip] as u32) + .wrapping_mul(2654435761) + ^ (input[ip + 1] as u32) + .wrapping_mul(2246822519) + ^ (input[ip + 2] as u32)) + as usize + & 0xFFFF; + + let ref_pos = hash_table[h]; + hash_table[h] = ip; + + // Check if back-reference is valid (within last 8191 bytes, matches >= 3 bytes). + let max_len = (input.len() - ip).min(264); + if ref_pos != usize::MAX + && ip > ref_pos + && (ip - ref_pos) <= 8191 + && ref_pos + 2 < input.len() + && input[ref_pos] == input[ip] + && input[ref_pos + 1] == input[ip + 1] + && input[ref_pos + 2] == input[ip + 2] + { + // Count match length (capped at 264). + let ofs = ip - ref_pos - 1; + let mut mlen = 3usize; + while mlen < max_len && input[ref_pos + mlen] == input[ip + mlen] { + mlen += 1; + } + + // Flush literals accumulated before this back-reference. + flush_literals(&mut output, input, lit_start, ip); + lit_start = ip + mlen; + ip += mlen; + + // Encode back-reference. + let len_code = mlen - 2; // stored as (len − 2) + if len_code < 7 { + output.push(((len_code << 5) | (ofs >> 8)) as u8); + } else { + output.push(((7 << 5) | (ofs >> 8)) as u8); + output.push((len_code - 7) as u8); + } + output.push((ofs & 0xFF) as u8); + } else { + ip += 1; + } + } + + // Flush any remaining literal bytes. + flush_literals(&mut output, input, lit_start, input.len()); + output +} + fn encode_mask_vers(layer: &Layer, mask: &[u8]) -> Vec { let tw: i32 = 64; let th: i32 = 64; @@ -366,12 +463,12 @@ fn encode_mask_vers(layer: &Layer, mask: &[u8]) -> Vec { let tidx = (y as usize) * (tw as usize) + (x as usize); let mut v = layer.mask_default_color; if let Some(mb) = layer.mask_bounds { - let m_top = mb[0] as i32; - let m_left = mb[1] as i32; + let m_top = mb[0] as i32; + let m_left = mb[1] as i32; let m_bottom = mb[2] as i32; - let m_right = mb[3] as i32; + let m_right = mb[3] as i32; if ly >= m_top && ly < m_bottom && lx >= m_left && lx < m_right { - let mask_w = m_right - m_left; + let mask_w = m_right - m_left; let mask_idx = ((ly - m_top) * mask_w + (lx - m_left)) as usize; if mask_idx < mask.len() { v = mask[mask_idx]; @@ -392,15 +489,12 @@ fn encode_mask_vers(layer: &Layer, mask: &[u8]) -> Vec { } if has_content { - let mut compressed = vec![1u8]; - let mut i = 0; - while i < tile_raw.len() { - let chunk = (tile_raw.len() - i).min(32); - compressed.push((chunk - 1) as u8); - compressed.extend_from_slice(&tile_raw[i..i + chunk]); - i += chunk; - } - tiles.push((tx, ty, compressed)); + // Same LZF format as raster tiles but PIXELSIZE=1. + let compressed = lzf_compress(&tile_raw); + let mut payload = Vec::with_capacity(1 + compressed.len()); + payload.push(1u8); // version byte (no delta-encode) + payload.extend_from_slice(&compressed); + tiles.push((tx, ty, payload)); } tx += tw; } @@ -412,13 +506,13 @@ fn encode_mask_vers(layer: &Layer, mask: &[u8]) -> Vec { output.extend_from_slice(b"TILEWIDTH 64\n"); output.extend_from_slice(b"TILEHEIGHT 64\n"); output.extend_from_slice(b"PIXELSIZE 1\n"); - output.extend_from_slice(b"DATA\n"); + output.extend_from_slice(format!("DATA {}\n", tiles.len()).as_bytes()); tiles.sort_by_key(|(x, y, _)| (*y, *x)); - for (tx, ty, tile_data) in tiles { - let header = format!("{},{},LZF,{}\n", tx, ty, tile_data.len()); + for (tx, ty, payload) in tiles { + let header = format!("{},{},LZF,{}\n", tx, ty, payload.len()); output.extend_from_slice(header.as_bytes()); - output.extend_from_slice(&tile_data); + output.extend_from_slice(&payload); } output diff --git a/hcie-kra/src/lib.rs b/hcie-kra/src/lib.rs index 9455435..74c2feb 100644 --- a/hcie-kra/src/lib.rs +++ b/hcie-kra/src/lib.rs @@ -627,6 +627,12 @@ fn try_import_krita_maindoc(archive: &mut zip::ZipArchive) -> Res f.read_to_string(&mut xml_content).map_err(|e| e.to_string())?; found = true; } + if !found { + if let Ok(mut f) = archive.by_name("layers.xml") { + f.read_to_string(&mut xml_content).map_err(|e| e.to_string())?; + found = true; + } + } if !found { return Ok(vec![]); } @@ -649,14 +655,25 @@ fn try_import_krita_maindoc(archive: &mut zip::ZipArchive) -> Res let name = e.name(); let tag_name = std::str::from_utf8(name.as_ref()).unwrap_or("").to_lowercase(); if tag_name == "image" { + let mut colorspace = String::new(); + let mut depth = String::new(); for attr in e.attributes() { let a = attr.map_err(|e| format!("XML attr error: {}", e))?; + let val = std::str::from_utf8(&a.value).unwrap_or(""); match a.key.as_ref() { - b"width" => width = std::str::from_utf8(&a.value).unwrap_or("800").parse().unwrap_or(800), - b"height" => height = std::str::from_utf8(&a.value).unwrap_or("600").parse().unwrap_or(600), + b"width" => width = val.parse().unwrap_or(800), + b"height" => height = val.parse().unwrap_or(600), + b"colorspacename" => colorspace = val.to_string(), + b"channeldepth" => depth = val.to_string(), _ => {} } } + if !colorspace.is_empty() && colorspace != "RGBA" && colorspace != "RGB" { + return Err(format!("Unsupported KRA color space: '{}' (only RGBA/RGB is supported)", colorspace)); + } + if !depth.is_empty() && depth != "U8" { + return Err(format!("Unsupported KRA channel depth: '{}' (only U8 is supported)", depth)); + } } else if tag_name == "layer" || tag_name == "paintlayer" || tag_name == "vectorlayer" @@ -787,7 +804,10 @@ fn try_import_krita_maindoc(archive: &mut zip::ZipArchive) -> Res let layer_path = find_krita_layer_file(&all_files, &info.filename, &info.nodetype); let zip_path = match layer_path { Some(ref path) => path, - None => continue, + None => { + log::warn!("KRA import: layer file not found for '{}' (filename={}, type={})", info.name, info.filename, info.nodetype); + continue; + } }; let mut buf = Vec::new(); @@ -805,13 +825,19 @@ fn try_import_krita_maindoc(archive: &mut zip::ZipArchive) -> Res let pixels = if is_vector { match rasterize_svg_to_rgba(&buf, width, height) { Ok(p) => p, - Err(_) => vec![0u8; (width * height * 4) as usize], + Err(e) => { + log::warn!("KRA import: SVG rasterization failed for layer '{}': {}", info.name, e); + vec![0u8; (width * height * 4) as usize] + } } } else if buf.starts_with(b"VERS") { let default_pixel = read_defaultpixel(archive, &info.filename); match parse_vers_tiles(&buf, width, height, info.x, info.y, default_pixel) { Ok(p) => p, - Err(_) => vec![0u8; (width * height * 4) as usize], + Err(e) => { + log::warn!("KRA import: VERS tile parse failed for layer '{}': {}", info.name, e); + vec![0u8; (width * height * 4) as usize] + } } } else { match image::load_from_memory(&buf) { @@ -834,7 +860,10 @@ fn try_import_krita_maindoc(archive: &mut zip::ZipArchive) -> Res } pixels } - Err(_) => vec![0u8; (width * height * 4) as usize], + Err(e) => { + log::warn!("KRA import: image decode failed for layer '{}': {}", info.name, e); + vec![0u8; (width * height * 4) as usize] + } } }; diff --git a/hcie_features.ods b/hcie_features.ods new file mode 100644 index 0000000000000000000000000000000000000000..7efd0d64b59db9c0c58d493bd7da47a212174caf GIT binary patch literal 18320 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 U7*7|F%-5u}MB60xnd8^!-(Hv0yZ`_I literal 0 HcmV?d00001