Compare commits
2 Commits
800da1872b
...
4e956e1939
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e956e1939 | |||
| 9bbb7038aa |
Generated
+2
-2
@@ -235,7 +235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustitch"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"png",
|
||||
"thiserror",
|
||||
@@ -250,7 +250,7 @@ checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||
|
||||
[[package]]
|
||||
name = "stitch-peek"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod palette;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::types::{ResolvedDesign, StitchCommand};
|
||||
use crate::types::{Color, RawDesign, ResolvedDesign, StitchCommand};
|
||||
use palette::JEF_PALETTE;
|
||||
|
||||
/// Parse a JEF (Janome) file from raw bytes into stitch commands and color info.
|
||||
@@ -15,7 +15,7 @@ use palette::JEF_PALETTE;
|
||||
///
|
||||
/// Stitch data: 2 bytes per stitch (signed i8 dx, dy).
|
||||
/// Control codes: 0x80 0x01 = color change, 0x80 0x02 = jump, 0x80 0x10 = end.
|
||||
type ParseResult = Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Error>;
|
||||
type ParseResult = Result<RawDesign, Error>;
|
||||
|
||||
pub fn parse(data: &[u8]) -> ParseResult {
|
||||
if data.len() < 116 {
|
||||
@@ -38,7 +38,7 @@ pub fn parse(data: &[u8]) -> ParseResult {
|
||||
|
||||
// Read color table starting at offset 116
|
||||
let color_table_start = 116;
|
||||
let mut colors = Vec::with_capacity(color_count);
|
||||
let mut colors: Vec<Color> = Vec::with_capacity(color_count);
|
||||
for i in 0..color_count {
|
||||
let entry_offset = color_table_start + i * 4;
|
||||
if entry_offset + 4 > data.len() {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::types::Color;
|
||||
|
||||
/// Janome thread color palette (78 entries).
|
||||
/// Index 0 is a fallback; indices 1-77 correspond to standard Janome thread colors.
|
||||
pub const JEF_PALETTE: [(u8, u8, u8); 78] = [
|
||||
pub const JEF_PALETTE: [Color; 78] = [
|
||||
(0, 0, 0), // 0: Unknown / Black
|
||||
(0, 0, 0), // 1: Black
|
||||
(255, 255, 255), // 2: White
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::types::Color;
|
||||
|
||||
/// Brother PEC thread color palette (65 entries).
|
||||
/// Index 0 is a fallback; indices 1-64 correspond to standard Brother thread colors.
|
||||
pub const PEC_PALETTE: [(u8, u8, u8); 65] = [
|
||||
pub const PEC_PALETTE: [Color; 65] = [
|
||||
(0, 0, 0), // 0: Unknown
|
||||
(14, 31, 124), // 1: Prussian Blue
|
||||
(10, 85, 163), // 2: Blue
|
||||
@@ -70,7 +72,7 @@ pub const PEC_PALETTE: [(u8, u8, u8); 65] = [
|
||||
|
||||
/// Default high-contrast palette for formats without embedded color info (DST, EXP).
|
||||
/// Colors cycle on each color change.
|
||||
pub const DEFAULT_PALETTE: [(u8, u8, u8); 12] = [
|
||||
pub const DEFAULT_PALETTE: [Color; 12] = [
|
||||
(0, 0, 0), // Black
|
||||
(237, 23, 31), // Red
|
||||
(10, 85, 163), // Blue
|
||||
@@ -86,12 +88,12 @@ pub const DEFAULT_PALETTE: [(u8, u8, u8); 12] = [
|
||||
];
|
||||
|
||||
/// Look up a PEC palette color by index, clamping to valid range.
|
||||
pub fn pec_color(idx: u8) -> (u8, u8, u8) {
|
||||
pub fn pec_color(idx: u8) -> Color {
|
||||
PEC_PALETTE[(idx as usize).min(PEC_PALETTE.len() - 1)]
|
||||
}
|
||||
|
||||
/// Build a color list for `n` thread slots by cycling through `DEFAULT_PALETTE`.
|
||||
pub fn default_colors(n: usize) -> Vec<(u8, u8, u8)> {
|
||||
pub fn default_colors(n: usize) -> Vec<Color> {
|
||||
(0..n)
|
||||
.map(|i| DEFAULT_PALETTE[i % DEFAULT_PALETTE.len()])
|
||||
.collect()
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
use crate::error::Error;
|
||||
use crate::pes::pec::{decode_stitches, parse_pec_header};
|
||||
use crate::types::{ResolvedDesign, StitchCommand};
|
||||
use crate::types::{Color, RawDesign, ResolvedDesign};
|
||||
|
||||
/// Parse a standalone PEC file (`#PEC0001` prefix + PEC data).
|
||||
pub fn parse(data: &[u8]) -> Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Error> {
|
||||
pub fn parse(data: &[u8]) -> Result<RawDesign, Error> {
|
||||
if data.len() < 8 || &data[0..8] != b"#PEC0001" {
|
||||
return Err(Error::InvalidHeader("missing #PEC0001 magic".into()));
|
||||
}
|
||||
@@ -13,7 +13,7 @@ pub fn parse(data: &[u8]) -> Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Err
|
||||
let commands = decode_stitches(&pec_data[stitch_offset..])?;
|
||||
|
||||
// Map PEC palette indices to RGB colors
|
||||
let colors: Vec<(u8, u8, u8)> = header
|
||||
let colors: Vec<Color> = header
|
||||
.color_indices
|
||||
.iter()
|
||||
.map(|&idx| crate::palette::pec_color(idx))
|
||||
|
||||
@@ -7,7 +7,7 @@ pub use pec::PecHeader;
|
||||
// Re-export shared types for backward compatibility
|
||||
pub use crate::error::Error;
|
||||
pub use crate::palette::PEC_PALETTE;
|
||||
pub use crate::types::{BoundingBox, ResolvedDesign, StitchCommand, StitchSegment};
|
||||
pub use crate::types::{BoundingBox, Color, ResolvedDesign, StitchCommand, StitchSegment};
|
||||
|
||||
pub struct PesDesign {
|
||||
pub header: PesHeader,
|
||||
@@ -37,7 +37,7 @@ pub fn parse(data: &[u8]) -> Result<PesDesign, Error> {
|
||||
|
||||
/// Convert parsed PES design into renderable segments with absolute coordinates.
|
||||
pub fn resolve(design: &PesDesign) -> Result<ResolvedDesign, Error> {
|
||||
let colors: Vec<(u8, u8, u8)> = design
|
||||
let colors: Vec<Color> = design
|
||||
.pec_header
|
||||
.color_indices
|
||||
.iter()
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
use crate::error::Error;
|
||||
use crate::types::{BoundingBox, ResolvedDesign, StitchCommand, StitchSegment};
|
||||
use crate::types::{BoundingBox, Color, ResolvedDesign, StitchCommand, StitchSegment};
|
||||
|
||||
/// Convert parsed stitch commands into renderable segments with absolute coordinates.
|
||||
pub fn resolve(
|
||||
commands: &[StitchCommand],
|
||||
colors: Vec<(u8, u8, u8)>,
|
||||
) -> Result<ResolvedDesign, Error> {
|
||||
pub fn resolve(commands: &[StitchCommand], colors: Vec<Color>) -> Result<ResolvedDesign, Error> {
|
||||
let mut segments = Vec::new();
|
||||
let mut x: f32 = 0.0;
|
||||
let mut y: f32 = 0.0;
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
use crate::error::Error;
|
||||
use crate::types::{ResolvedDesign, StitchCommand};
|
||||
use crate::types::{Color, RawDesign, ResolvedDesign, StitchCommand};
|
||||
|
||||
const STITCH_DATA_OFFSET: usize = 0x1D78;
|
||||
|
||||
/// Janome SEW thread color palette (first 80 entries).
|
||||
const SEW_PALETTE: [(u8, u8, u8); 80] = [
|
||||
const SEW_PALETTE: [Color; 80] = [
|
||||
(0, 0, 0), // 0: Unknown
|
||||
(0, 0, 0), // 1: Black
|
||||
(255, 255, 255), // 2: White
|
||||
@@ -100,7 +100,7 @@ const SEW_PALETTE: [(u8, u8, u8); 80] = [
|
||||
/// - 0x10: normal stitch (read 2 signed bytes)
|
||||
/// - other: end
|
||||
/// - Y is negated
|
||||
pub fn parse(data: &[u8]) -> Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Error> {
|
||||
pub fn parse(data: &[u8]) -> Result<RawDesign, Error> {
|
||||
if data.len() < STITCH_DATA_OFFSET + 4 {
|
||||
return Err(Error::TooShort {
|
||||
expected: STITCH_DATA_OFFSET + 4,
|
||||
@@ -114,7 +114,7 @@ pub fn parse(data: &[u8]) -> Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Err
|
||||
}
|
||||
|
||||
// Read thread palette indices
|
||||
let colors: Vec<(u8, u8, u8)> = (0..color_count)
|
||||
let colors: Vec<Color> = (0..color_count)
|
||||
.map(|i| {
|
||||
let off = 2 + i * 2;
|
||||
if off + 1 < data.len() {
|
||||
|
||||
@@ -7,6 +7,9 @@ pub enum StitchCommand {
|
||||
End,
|
||||
}
|
||||
|
||||
pub type Color = (u8, u8, u8);
|
||||
pub type RawDesign = (Vec<StitchCommand>, Vec<Color>);
|
||||
|
||||
pub struct StitchSegment {
|
||||
pub x0: f32,
|
||||
pub y0: f32,
|
||||
@@ -24,6 +27,6 @@ pub struct BoundingBox {
|
||||
|
||||
pub struct ResolvedDesign {
|
||||
pub segments: Vec<StitchSegment>,
|
||||
pub colors: Vec<(u8, u8, u8)>,
|
||||
pub colors: Vec<Color>,
|
||||
pub bounds: BoundingBox,
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
use crate::error::Error;
|
||||
use crate::types::{ResolvedDesign, StitchCommand};
|
||||
use crate::types::{Color, RawDesign, ResolvedDesign, StitchCommand};
|
||||
|
||||
const HEADER_SIZE: usize = 256;
|
||||
|
||||
@@ -15,7 +15,7 @@ const HEADER_SIZE: usize = 256;
|
||||
/// - 0x08 or 0x0A..0x17: color change
|
||||
/// - 0x7F: end of data
|
||||
/// - Color table after stitch data: skip 2 bytes, then color_count × i32 BE (0x00RRGGBB)
|
||||
pub fn parse(data: &[u8]) -> Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Error> {
|
||||
pub fn parse(data: &[u8]) -> Result<RawDesign, Error> {
|
||||
if data.len() < HEADER_SIZE + 2 {
|
||||
return Err(Error::TooShort {
|
||||
expected: HEADER_SIZE + 2,
|
||||
@@ -99,7 +99,7 @@ pub fn parse(data: &[u8]) -> Result<(Vec<StitchCommand>, Vec<(u8, u8, u8)>), Err
|
||||
commands.push(StitchCommand::End);
|
||||
|
||||
// Read color table: color_count × i32 BE (0x00RRGGBB)
|
||||
let colors = if color_table_start + color_count * 4 <= data.len() {
|
||||
let colors: Vec<Color> = if color_table_start + color_count * 4 <= data.len() {
|
||||
(0..color_count)
|
||||
.map(|c| {
|
||||
let base = color_table_start + c * 4;
|
||||
|
||||
Reference in New Issue
Block a user