more msla corrections
This commit is contained in:
parent
50e0a4674b
commit
50e0758f99
2 changed files with 89 additions and 54 deletions
|
|
@ -11,6 +11,10 @@
|
|||
### Previous (Incorrect) Description
|
||||
"RLE125: Simple 2-byte fixed format (color, length), max 125 pixels"
|
||||
|
||||
This was doubly misleading: CTB does not use RLE125, and UVtools' CBDDLP/PWS
|
||||
RLE125 implementation is a 1-byte bit-plane run format, not a two-byte
|
||||
`(color, length)` pair.
|
||||
|
||||
### Actual Implementation (from ChituboxFile.cs:825-908)
|
||||
|
||||
CTB uses a **7-bit grayscale variable-length RLE** with optional encryption, NOT simple RLE125.
|
||||
|
|
@ -236,9 +240,9 @@ Encoded: E4 C0 4E 20
|
|||
- Output: [0xE4] [0xC0] [0x4E] [0x20] (4 bytes)
|
||||
```
|
||||
|
||||
#### Key Differences from Simple RLE125
|
||||
#### Key Differences from the Previous Two-Byte Description
|
||||
|
||||
| Feature | Simple RLE125 | CTB Variable RLE |
|
||||
| Feature | Previous two-byte description | CTB Variable RLE |
|
||||
|---------|---------------|------------------|
|
||||
| **Color depth** | 8-bit (0-255) | 7-bit (0-127) |
|
||||
| **Max run (1-byte)** | 125 | 127 |
|
||||
|
|
@ -316,12 +320,20 @@ public override uint[] GetAvailableVersionsForExtension(string? extension)
|
|||
// AvailableVersions = [VERSION_1, VERSION_515, VERSION_516, VERSION_517, VERSION_518]
|
||||
```
|
||||
|
||||
**PrusaSlicer Profile:**
|
||||
```ini
|
||||
# PrusaSlicer/printer/Anycubic Photon Mono 4.ini
|
||||
FILEFORMAT_PM4N
|
||||
# No FILEVERSION_517 entry is present.
|
||||
```
|
||||
|
||||
### The Ambiguity
|
||||
|
||||
**`.pm4n` extension:**
|
||||
- ✓ Registered as valid extension
|
||||
- ✓ Maps to PhotonMono4 machine
|
||||
- ✗ **NOT** explicitly assigned to any version in `GetAvailableVersionsForExtension`
|
||||
- ✗ PrusaSlicer profile declares `FILEFORMAT_PM4N` but does not declare `FILEVERSION_517`
|
||||
- Returns all versions: `[1, 515, 516, 517, 518]`
|
||||
|
||||
### Possible Interpretations
|
||||
|
|
@ -335,7 +347,7 @@ public override uint[] GetAvailableVersionsForExtension(string? extension)
|
|||
**User's current implementation treats `.pm4n` as v517**, which is:
|
||||
- ✓ Consistent with similar naming (.pm3n, .pm5 are v517)
|
||||
- ✓ Logical based on product generation
|
||||
- ⚠️ **UNVERIFIED** by UVtools code (could be any version)
|
||||
- ⚠️ **UNVERIFIED** by UVtools code/profile metadata (could be any version)
|
||||
|
||||
### Recommendations
|
||||
|
||||
|
|
@ -414,13 +426,14 @@ if (extension == "pm4n") {
|
|||
- Extension mapping: `AnycubicFile.cs:1155-1191` (GetAvailableVersionsForExtension)
|
||||
- Machine detection: `AnycubicFile.cs:1731-1733` (FileEndsWith check)
|
||||
- Version constants: `AnycubicFile.cs:30-33` (VERSION_515-518)
|
||||
- Photon Mono 4 PrusaSlicer profile: `PrusaSlicer/printer/Anycubic Photon Mono 4.ini` (`FILEFORMAT_PM4N`, no `FILEVERSION_517`)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**CTB RLE:**
|
||||
- ❌ Previous: Simple RLE125 (2 bytes/run, max 125)
|
||||
- ❌ Previous: Incorrectly described as simple two-byte RLE125
|
||||
- ✅ Correct: Variable-length 7-bit grayscale RLE (1-5 bytes/run, max 268M, optional encryption)
|
||||
|
||||
**.pm4n Version:**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# mSLA File Format Reference
|
||||
|
||||
**Generated:** 2026-05-16
|
||||
**Total Formats:** 32
|
||||
**Total Formats:** 31 registered in UVtools `AvailableFormats` (plus an unregistered CXDLPv1 implementation)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -16,16 +16,17 @@
|
|||
| ChituboxZipFile | ✓ | ✓ | zip | - | No | PNG | G-code based |
|
||||
| AnycubicPhotonSFile | ✓ | ✓ | photons | - | No | RLE128 | Legacy Photon S |
|
||||
| **Anycubic Family - Legacy (v1, v515-516)** |
|
||||
| AnycubicFile (PWS) | ✓ | ✓ | pws, pwx | 1 | Yes (22) | RLE1 (125) | PhotonS, PhotonX |
|
||||
| AnycubicFile (PW0) | ✓ | ✓ | pw0 | 1 | Yes (22) | Nibble RLE4 | PhotonZero |
|
||||
| AnycubicFile (PWS) | ✓ | ✓ | pws | 1 | Yes (22) | 1-byte bit-plane RLE125 | PhotonS |
|
||||
| AnycubicFile (PW0/PWX) | ✓ | ✓ | pw0, pwx | 1 | Yes (22) | Nibble RLE4 | PhotonZero, PhotonX |
|
||||
| **Anycubic Family - Modern (v515-518)** |
|
||||
| AnycubicFile (v515) | ✓ | ✓ | pwmx, pwmo, dl2p, pwmb, pmx2 | 515-517 | Yes (22) | Nibble RLE4 | Mono series |
|
||||
| AnycubicFile (v515-v516) | ✓ | ✓ | dlp, pwmx, pwmo, pwms, pmsq, pwma, pm3, pm3m | varies | Yes (22) | Nibble RLE4 | Ultra, Mono, M3 family |
|
||||
| AnycubicFile (v515-v517) | ✓ | ✓ | dl2p, pwmb, pmx2, pm3r | varies | Yes (22) | Nibble RLE4 | D2, X2, M3 Plus/Premium |
|
||||
| AnycubicFile (v517) | ✓ | ✓ | pm3n, pm5, px6s | 517 | Yes (22) | Nibble RLE4 | Mono 2, X6Ks, M5 |
|
||||
| AnycubicFile (v518) | ✓ | ✓ | pm5s, m5sp | 518 | Yes (22) | Nibble RLE4 | Mono M5s/Pro |
|
||||
| AnycubicFile (all) | ✓ | ✓ | dlp, pwms, pwma, pmsq, pm3, pm3m, pm3r, pwc | varies | Yes (22) | Nibble RLE4 | Additional models |
|
||||
| AnycubicFile (unversioned fallback) | ✓ | ✓ | pm4n, pwc | 1, 515-518 | Yes (22) | Nibble RLE4 | Registered but not explicitly version-pinned |
|
||||
| AnycubicZipFile | ✓ | ✓ | pm4u, pm7, pm7m, pwsz, pp1, pp1m | - | No | PNG | ZIP with JSON |
|
||||
| **Creality Family** |
|
||||
| CrealityCXDLPv1File | ✓ | ✓ | v1.cxdlp | 1 | No | RLE | Legacy |
|
||||
| CrealityCXDLPv1File | ✓ | ✓ | v1.cxdlp | 1 | No | RLE | Legacy implementation present but not registered in `AvailableFormats` |
|
||||
| CrealityCXDLPFile | ✓ | ✓ | cxdlp | 2-3 (def: 3) | Yes | RLE | HALOT series |
|
||||
| CrealityCXDLPv4File | ✓ | ✓ | cxdlpv4 | 4 | Yes | RLE | Current |
|
||||
| **Other Binary Formats** |
|
||||
|
|
@ -188,26 +189,30 @@ Same header structure as CTB v3, with additional support for:
|
|||
|
||||
##### 2.1.1 PWS (Photon / Photon S)
|
||||
|
||||
**Extensions:** `.pws`, `.pwx`
|
||||
**Extensions:** `.pws`
|
||||
**Version:** 1
|
||||
**RLE:** RLE1 (limit: 125)
|
||||
**Machines:** PhotonS, PhotonX
|
||||
**Machines:** PhotonS
|
||||
|
||||
**RLE1 Encoding (PWS):**
|
||||
```
|
||||
For each run (2 bytes):
|
||||
byte[0] = color (0x00 = black, 0xFF = white)
|
||||
byte[1] = length (1-125)
|
||||
For each run (1 byte):
|
||||
bit 7 = white/on flag
|
||||
bits 0-6 = repeat count
|
||||
|
||||
Runs are emitted per anti-aliasing bit-plane and then accumulated back to
|
||||
8-bit grayscale. UVtools' encoder splits runs at 0x7D (125), although the
|
||||
stored count field is 7 bits.
|
||||
|
||||
Constant: RLE1EncodingLimit = 0x7D (125)
|
||||
```
|
||||
|
||||
##### 2.1.2 PW0 (Photon Zero) - CORRECTED
|
||||
##### 2.1.2 PW0/PWX (Photon Zero / Photon X) - CORRECTED
|
||||
|
||||
**Extensions:** `.pw0`
|
||||
**Extensions:** `.pw0`, `.pwx`
|
||||
**Version:** 1
|
||||
**RLE:** Nibble-coded RLE4 (limit: 4095)
|
||||
**Machine:** PhotonZero
|
||||
**Machines:** PhotonZero, PhotonX
|
||||
|
||||
**Nibble-Coded RLE4 Encoding (CORRECTED from UVtools implementation):**
|
||||
|
||||
|
|
@ -332,9 +337,12 @@ Gray (0xAA) run of 5 pixels:
|
|||
|
||||
All modern Anycubic formats use **Nibble-coded RLE4** encoding.
|
||||
|
||||
##### 2.2.1 Version 515-516 Formats
|
||||
##### 2.2.1 Version-Mapped Formats Before v518
|
||||
|
||||
**Extensions:** `.pwmx`, `.pwmo`, `.dl2p`, `.pwmb`, `.pmx2`, `.dlp`, `.pwms`, `.pwma`, `.pmsq`, `.pm3`, `.pm3m`, `.pm3r`
|
||||
**Extension version sets from UVtools `GetAvailableVersionsForExtension`:**
|
||||
- `.pwmx`, `.pwmo`, `.pwms`, `.pmsq`, `.dlp`: v1, v515, v516
|
||||
- `.pwma`, `.pm3`, `.pm3m`: v515, v516
|
||||
- `.pwmb`, `.dl2p`, `.pmx2`, `.pm3r`: v515, v516, v517
|
||||
|
||||
**Machines:**
|
||||
- PhotonMono, PhotonMonoX, PhotonMonoX2
|
||||
|
|
@ -359,29 +367,30 @@ All modern Anycubic formats use **Nibble-coded RLE4** encoding.
|
|||
- PhotonMono2 (.pm3n)
|
||||
- PhotonMonoM5 (.pm5)
|
||||
- PhotonMonoX6Ks (.px6s)
|
||||
- PhotonMono4 (.pm4n) is mapped as a machine, but its source and PrusaSlicer profile omit an explicit file version
|
||||
|
||||
**New Features:**
|
||||
- Additional preview/model/software metadata compared with earlier versions
|
||||
- Preview sizes: 224x168, 330x190
|
||||
- `SOFTWARE` and `MODEL` sections compared with earlier versions
|
||||
- `MACHINE` property field count rises to 7
|
||||
- Thumbnail defaults are 224x168 and 330x190; v517 writes the first `PREVIEW` table
|
||||
- `HEADER` table length: 92 bytes
|
||||
- `LayerDef` entries remain 32 bytes each
|
||||
|
||||
##### 2.2.3 Version 518 Formats
|
||||
|
||||
**Extensions:** `.pm5s`, `.m5sp`, `.pwc`
|
||||
**Extensions:** `.pm5s`, `.m5sp` (`.pwc` is registered as Anycubic Custom Machine but falls through to all available versions)
|
||||
|
||||
**Machines:**
|
||||
- PhotonMonoM5s (.pm5s)
|
||||
- PhotonMonoM5sPro (.m5sp)
|
||||
- AnycubicCustomMachine (.pwc)
|
||||
|
||||
**New Features:**
|
||||
- 9 preview images
|
||||
- 11 distinct printer profiles
|
||||
- 11 FileMark table entries
|
||||
- second `PREVIEW2` table may be present
|
||||
- Preview sizes: 224x168, 330x190
|
||||
- `HEADER` table length: 96 bytes
|
||||
- `LayerDef` entries remain 32 bytes each; `SUBIMGS` sublayer records may also be present
|
||||
- 15 slicer metadata fields
|
||||
- `MACHINE` property field count rises to 15
|
||||
|
||||
**Anycubic FileMark and Section Table (v515+):**
|
||||
```
|
||||
|
|
@ -437,14 +446,14 @@ Each section starts with a 12-byte null-padded table name followed by a 32-bit t
|
|||
|
||||
| Format | Name | Bytes/Run | Max Repeat | Color Bits | Notes |
|
||||
|--------|------|-----------|------------|------------|-------|
|
||||
| RLE1 (PWS) | Run-Length 1 | 2 (fixed) | 125 | 8 | Color, Length |
|
||||
| RLE1/RLE125 (PWS/CBDDLP) | Bit-plane run-length | 1 (fixed) | 125 encoded / 127 stored | 1 per AA plane | bit 7 on/off, low 7 bits repeat |
|
||||
| RLE4 (PW0) | Nibble-coded | 1-2 (variable) | 4095 (B/W), 15 (gray) | 8 | Optimized variable-length |
|
||||
| CTB variable RLE | 7-bit grayscale variable RLE | 1-5 (variable) | 268,435,455 | 7 stored / 8 expanded | CTB v3+ layer data |
|
||||
| RLE128 (PhotonS) | Run-Length 128 | 2 (fixed) | 128 | 8 | Length, Color (BE) |
|
||||
| RLE+Delim (GOO) | Delimited | 5 (+ delim) | 65535 | 8 | 0x55 + Color + Length16 + 0x0D0A |
|
||||
|
||||
**Performance Comparison:**
|
||||
- **RLE1/RLE125/RLE128:** Simple, predictable, easy to implement
|
||||
- **RLE1/RLE125/RLE128:** Simple, predictable, easy to implement, but PWS/CBDDLP are bit-plane encodings rather than full grayscale runs
|
||||
- **CTB variable RLE:** Better compression and grayscale AA, plus optional seed-derived XOR
|
||||
- **Nibble RLE4:** Best compression for grayscale AA, complex encoding
|
||||
- **RLE+Delim:** Highest max repeat, overhead from delimiters
|
||||
|
|
@ -618,43 +627,57 @@ PrinterModel = SL1
|
|||
|
||||
### Essential Algorithms (Pseudocode)
|
||||
|
||||
#### RLE125 Decoder (CBDDLP)
|
||||
#### RLE125 Bit-Plane Decoder (CBDDLP/PWS)
|
||||
```python
|
||||
def decode_rle125(data: bytes, width: int, height: int) -> bytes:
|
||||
pixels = bytearray(width * height)
|
||||
pixel_index = 0
|
||||
def decode_rle125_bitplanes(data: bytes, width: int, height: int, aa: int = 1) -> bytes:
|
||||
levels = bytearray(width * height)
|
||||
data_index = 0
|
||||
|
||||
while data_index < len(data):
|
||||
color = data[data_index]
|
||||
length = data[data_index + 1]
|
||||
data_index += 2
|
||||
for _plane in range(aa):
|
||||
pixel_index = 0
|
||||
while pixel_index < len(levels):
|
||||
code = data[data_index]
|
||||
data_index += 1
|
||||
|
||||
for _ in range(length):
|
||||
pixels[pixel_index] = color
|
||||
pixel_index += 1
|
||||
length = code & 0x7F
|
||||
if code & 0x80:
|
||||
for i in range(length):
|
||||
levels[pixel_index + i] += 1
|
||||
|
||||
pixel_index += length
|
||||
|
||||
pixels = bytearray(len(levels))
|
||||
scale = 256 // aa
|
||||
for i, level in enumerate(levels):
|
||||
value = level * scale
|
||||
pixels[i] = value - 1 if value else 0
|
||||
|
||||
return pixels
|
||||
```
|
||||
|
||||
#### RLE125 Encoder (CBDDLP)
|
||||
#### RLE125 Bit-Plane Encoder (Single Plane)
|
||||
```python
|
||||
def encode_rle125(pixels: bytes) -> bytes:
|
||||
def encode_rle125_bitplane(bits: list[bool]) -> bytes:
|
||||
result = bytearray()
|
||||
i = 0
|
||||
run_on = False
|
||||
run = 0
|
||||
|
||||
while i < len(pixels):
|
||||
color = pixels[i]
|
||||
length = 1
|
||||
def flush():
|
||||
nonlocal run
|
||||
if run:
|
||||
result.append(run | (0x80 if run_on else 0))
|
||||
run = 0
|
||||
|
||||
while (i + length < len(pixels) and
|
||||
pixels[i + length] == color and
|
||||
length < 125):
|
||||
length += 1
|
||||
for bit in bits:
|
||||
if bit == run_on and run < 0x7D:
|
||||
run += 1
|
||||
continue
|
||||
|
||||
result.append(color)
|
||||
result.append(length)
|
||||
i += length
|
||||
flush()
|
||||
run_on = bit
|
||||
run = 1
|
||||
|
||||
flush()
|
||||
|
||||
return result
|
||||
```
|
||||
|
|
@ -883,4 +906,3 @@ License: AGPL-3.0
|
|||
|
||||
**Document Version:** 2.0
|
||||
**Generated:** 2026-05-16
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue