roxify 1.12.8 → 1.12.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +1 -1
- package/native/encoder.rs +39 -100
- package/native/streaming.rs +6 -8
- package/native/streaming_encode.rs +2 -1
- package/package.json +1 -1
- package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
package/Cargo.toml
CHANGED
package/native/encoder.rs
CHANGED
|
@@ -199,7 +199,7 @@ fn encode_to_png_with_encryption_name_and_filelist_internal(
|
|
|
199
199
|
let padded_len = raw_payload_len + padding_needed;
|
|
200
200
|
|
|
201
201
|
let marker_start_len = 12;
|
|
202
|
-
let
|
|
202
|
+
let marker_end_bytes = 9;
|
|
203
203
|
|
|
204
204
|
let data_with_markers_len = marker_start_len + padded_len;
|
|
205
205
|
let data_pixels = (data_with_markers_len + 2) / 3;
|
|
@@ -212,9 +212,9 @@ fn encode_to_png_with_encryption_name_and_filelist_internal(
|
|
|
212
212
|
let height = side;
|
|
213
213
|
|
|
214
214
|
let total_data_bytes = width * height * 3;
|
|
215
|
-
let marker_end_pos =
|
|
215
|
+
let marker_end_pos = total_data_bytes - marker_end_bytes;
|
|
216
216
|
|
|
217
|
-
let flat = build_flat_pixel_buffer(&meta_pixel,
|
|
217
|
+
let flat = build_flat_pixel_buffer(&meta_pixel, marker_end_pos, total_data_bytes);
|
|
218
218
|
drop(meta_pixel);
|
|
219
219
|
|
|
220
220
|
let row_bytes = width * 3;
|
|
@@ -226,9 +226,7 @@ fn encode_to_png_with_encryption_name_and_filelist_internal(
|
|
|
226
226
|
|
|
227
227
|
fn build_flat_pixel_buffer(
|
|
228
228
|
meta_pixel: &[u8],
|
|
229
|
-
_padding_needed: usize,
|
|
230
229
|
marker_end_pos: usize,
|
|
231
|
-
marker_end_len: usize,
|
|
232
230
|
total_data_bytes: usize,
|
|
233
231
|
) -> Vec<u8> {
|
|
234
232
|
let mut flat = vec![0u8; total_data_bytes];
|
|
@@ -246,24 +244,14 @@ fn build_flat_pixel_buffer(
|
|
|
246
244
|
|
|
247
245
|
flat[pos..pos + meta_pixel.len()].copy_from_slice(meta_pixel);
|
|
248
246
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
flat[off] = m.0; flat[off + 1] = m.1; flat[off + 2] = m.2;
|
|
253
|
-
}
|
|
247
|
+
for (i, m) in MARKER_END.iter().enumerate() {
|
|
248
|
+
let off = marker_end_pos + i * 3;
|
|
249
|
+
flat[off] = m.0; flat[off + 1] = m.1; flat[off + 2] = m.2;
|
|
254
250
|
}
|
|
255
251
|
|
|
256
252
|
flat
|
|
257
253
|
}
|
|
258
254
|
|
|
259
|
-
fn build_meta_pixel(payload: &[u8]) -> Result<Vec<u8>> {
|
|
260
|
-
build_meta_pixel_with_name(payload, None)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
fn build_meta_pixel_with_name(payload: &[u8], name: Option<&str>) -> Result<Vec<u8>> {
|
|
264
|
-
build_meta_pixel_with_name_and_filelist(payload, name, None)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
255
|
fn build_meta_pixel_with_name_and_filelist(payload: &[u8], name: Option<&str>, file_list: Option<&str>) -> Result<Vec<u8>> {
|
|
268
256
|
let version = 1u8;
|
|
269
257
|
let name_bytes = name.map(|n| n.as_bytes()).unwrap_or(&[]);
|
|
@@ -333,38 +321,6 @@ fn write_chunk(out: &mut Vec<u8>, chunk_type: &[u8; 4], data: &[u8]) -> Result<(
|
|
|
333
321
|
Ok(())
|
|
334
322
|
}
|
|
335
323
|
|
|
336
|
-
fn create_raw_deflate(data: &[u8]) -> Vec<u8> {
|
|
337
|
-
const MAX_BLOCK: usize = 65535;
|
|
338
|
-
let num_blocks = (data.len() + MAX_BLOCK - 1) / MAX_BLOCK;
|
|
339
|
-
let total_size = 2 + num_blocks * 5 + data.len() + 4;
|
|
340
|
-
let mut result = Vec::with_capacity(total_size);
|
|
341
|
-
|
|
342
|
-
result.push(0x78);
|
|
343
|
-
result.push(0x01);
|
|
344
|
-
|
|
345
|
-
let mut offset = 0;
|
|
346
|
-
while offset < data.len() {
|
|
347
|
-
let chunk_size = (data.len() - offset).min(MAX_BLOCK);
|
|
348
|
-
let is_last = offset + chunk_size >= data.len();
|
|
349
|
-
|
|
350
|
-
let header = [
|
|
351
|
-
if is_last { 0x01 } else { 0x00 },
|
|
352
|
-
chunk_size as u8,
|
|
353
|
-
(chunk_size >> 8) as u8,
|
|
354
|
-
!chunk_size as u8,
|
|
355
|
-
(!(chunk_size >> 8)) as u8,
|
|
356
|
-
];
|
|
357
|
-
result.extend_from_slice(&header);
|
|
358
|
-
result.extend_from_slice(&data[offset..offset + chunk_size]);
|
|
359
|
-
offset += chunk_size;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
let adler = crate::core::adler32_bytes(data);
|
|
363
|
-
result.extend_from_slice(&adler.to_be_bytes());
|
|
364
|
-
|
|
365
|
-
result
|
|
366
|
-
}
|
|
367
|
-
|
|
368
324
|
fn create_raw_deflate_from_rows(flat: &[u8], row_bytes: usize, height: usize) -> Vec<u8> {
|
|
369
325
|
let stride = row_bytes + 1;
|
|
370
326
|
let scanlines_total = height * stride;
|
|
@@ -410,35 +366,6 @@ fn create_raw_deflate_from_rows(flat: &[u8], row_bytes: usize, height: usize) ->
|
|
|
410
366
|
result
|
|
411
367
|
}
|
|
412
368
|
|
|
413
|
-
fn predict_best_format(data: &[u8]) -> ImageFormat {
|
|
414
|
-
if data.len() < 2048 {
|
|
415
|
-
return ImageFormat::Png;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
let sample_size = data.len().min(4096);
|
|
419
|
-
let sample = &data[..sample_size];
|
|
420
|
-
|
|
421
|
-
let entropy = calculate_shannon_entropy(sample);
|
|
422
|
-
let repetition_score = detect_repetition_patterns(sample);
|
|
423
|
-
|
|
424
|
-
let unique_bytes = count_unique_bytes(sample);
|
|
425
|
-
let unique_ratio = unique_bytes as f64 / 256.0;
|
|
426
|
-
|
|
427
|
-
let is_sequential = detect_sequential_pattern(sample);
|
|
428
|
-
|
|
429
|
-
if entropy > 7.8 {
|
|
430
|
-
ImageFormat::Png
|
|
431
|
-
} else if is_sequential || repetition_score > 0.2 {
|
|
432
|
-
ImageFormat::JpegXL
|
|
433
|
-
} else if unique_ratio < 0.3 && entropy < 6.5 {
|
|
434
|
-
ImageFormat::JpegXL
|
|
435
|
-
} else if entropy < 5.5 {
|
|
436
|
-
ImageFormat::JpegXL
|
|
437
|
-
} else {
|
|
438
|
-
ImageFormat::Png
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
369
|
fn detect_sequential_pattern(data: &[u8]) -> bool {
|
|
443
370
|
if data.len() < 256 {
|
|
444
371
|
return false;
|
|
@@ -548,6 +475,39 @@ mod tests {
|
|
|
548
475
|
if decompressed.starts_with(b"ROX1") { decompressed = decompressed[4..].to_vec(); }
|
|
549
476
|
assert_eq!(decompressed, data);
|
|
550
477
|
}
|
|
478
|
+
|
|
479
|
+
#[test]
|
|
480
|
+
fn test_marker_end_in_last_3_pixels() {
|
|
481
|
+
use image::ImageReader;
|
|
482
|
+
use std::io::Cursor;
|
|
483
|
+
|
|
484
|
+
for size in &[11, 100, 1000, 5000, 50000] {
|
|
485
|
+
let data: Vec<u8> = (0..*size).map(|i| (i % 256) as u8).collect();
|
|
486
|
+
|
|
487
|
+
let png_raw = encode_to_png_raw(&data, 3).expect("encode raw");
|
|
488
|
+
let png_auto = encode_to_png(&data, 3).expect("encode auto");
|
|
489
|
+
|
|
490
|
+
for (label, png) in &[("raw", &png_raw), ("auto", &png_auto)] {
|
|
491
|
+
let reader = ImageReader::new(Cursor::new(*png))
|
|
492
|
+
.with_guessed_format().unwrap();
|
|
493
|
+
let img = reader.decode().unwrap();
|
|
494
|
+
let rgb = img.to_rgb8();
|
|
495
|
+
let w = rgb.width();
|
|
496
|
+
let h = rgb.height();
|
|
497
|
+
|
|
498
|
+
let p1 = rgb.get_pixel(w - 3, h - 1);
|
|
499
|
+
let p2 = rgb.get_pixel(w - 2, h - 1);
|
|
500
|
+
let p3 = rgb.get_pixel(w - 1, h - 1);
|
|
501
|
+
|
|
502
|
+
assert_eq!([p1[0], p1[1], p1[2]], [0, 0, 255],
|
|
503
|
+
"MARKER_END pixel 0 (blue) wrong for {}@size={}, got {:?}", label, size, p1);
|
|
504
|
+
assert_eq!([p2[0], p2[1], p2[2]], [0, 255, 0],
|
|
505
|
+
"MARKER_END pixel 1 (green) wrong for {}@size={}, got {:?}", label, size, p2);
|
|
506
|
+
assert_eq!([p3[0], p3[1], p3[2]], [255, 0, 0],
|
|
507
|
+
"MARKER_END pixel 2 (red) wrong for {}@size={}, got {:?}", label, size, p3);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
551
511
|
}
|
|
552
512
|
|
|
553
513
|
fn detect_repetition_patterns(data: &[u8]) -> f64 {
|
|
@@ -575,27 +535,6 @@ fn detect_repetition_patterns(data: &[u8]) -> f64 {
|
|
|
575
535
|
}
|
|
576
536
|
}
|
|
577
537
|
|
|
578
|
-
fn optimize_format(png_data: &[u8]) -> Result<Vec<u8>> {
|
|
579
|
-
let formats = [
|
|
580
|
-
("webp", optimize_to_webp(png_data)),
|
|
581
|
-
("jxl", optimize_to_jxl(png_data)),
|
|
582
|
-
];
|
|
583
|
-
|
|
584
|
-
let mut best = png_data.to_vec();
|
|
585
|
-
let mut best_size = png_data.len();
|
|
586
|
-
|
|
587
|
-
for (_name, result) in formats {
|
|
588
|
-
if let Ok(optimized) = result {
|
|
589
|
-
if optimized.len() < best_size {
|
|
590
|
-
best = optimized;
|
|
591
|
-
best_size = best.len();
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
Ok(best)
|
|
597
|
-
}
|
|
598
|
-
|
|
599
538
|
fn optimize_to_webp(png_data: &[u8]) -> Result<Vec<u8>> {
|
|
600
539
|
use std::fs;
|
|
601
540
|
|
package/native/streaming.rs
CHANGED
|
@@ -41,6 +41,7 @@ pub fn encode_to_png_file(
|
|
|
41
41
|
let padded_len = raw_payload_len + padding_needed;
|
|
42
42
|
|
|
43
43
|
let marker_start_len = 12;
|
|
44
|
+
let marker_end_bytes = 9;
|
|
44
45
|
let data_with_markers_len = marker_start_len + padded_len;
|
|
45
46
|
let data_pixels = (data_with_markers_len + 2) / 3;
|
|
46
47
|
let end_marker_pixels = 3;
|
|
@@ -52,9 +53,9 @@ pub fn encode_to_png_file(
|
|
|
52
53
|
let height = side;
|
|
53
54
|
let row_bytes = width * 3;
|
|
54
55
|
let total_data_bytes = width * height * 3;
|
|
55
|
-
let marker_end_pos =
|
|
56
|
+
let marker_end_pos = total_data_bytes - marker_end_bytes;
|
|
56
57
|
|
|
57
|
-
let flat = build_flat_buffer(&meta_pixel,
|
|
58
|
+
let flat = build_flat_buffer(&meta_pixel, marker_end_pos, total_data_bytes);
|
|
58
59
|
drop(meta_pixel);
|
|
59
60
|
|
|
60
61
|
let stride = row_bytes + 1;
|
|
@@ -149,7 +150,6 @@ fn write_idat_direct<W: Write>(
|
|
|
149
150
|
|
|
150
151
|
fn build_flat_buffer(
|
|
151
152
|
meta_pixel: &[u8],
|
|
152
|
-
_padding_needed: usize,
|
|
153
153
|
marker_end_pos: usize,
|
|
154
154
|
total_data_bytes: usize,
|
|
155
155
|
) -> Vec<u8> {
|
|
@@ -166,11 +166,9 @@ fn build_flat_buffer(
|
|
|
166
166
|
pos += PIXEL_MAGIC.len();
|
|
167
167
|
flat[pos..pos + meta_pixel.len()].copy_from_slice(meta_pixel);
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
flat[off] = m.0; flat[off + 1] = m.1; flat[off + 2] = m.2;
|
|
173
|
-
}
|
|
169
|
+
for (i, m) in MARKER_END.iter().enumerate() {
|
|
170
|
+
let off = marker_end_pos + i * 3;
|
|
171
|
+
flat[off] = m.0; flat[off + 1] = m.1; flat[off + 2] = m.2;
|
|
174
172
|
}
|
|
175
173
|
|
|
176
174
|
flat
|
|
@@ -163,6 +163,7 @@ fn write_png_from_zst(
|
|
|
163
163
|
let padded_len = raw_payload_len + padding_needed;
|
|
164
164
|
|
|
165
165
|
let marker_start_len = 12;
|
|
166
|
+
let marker_end_bytes = 9;
|
|
166
167
|
let data_with_markers_len = marker_start_len + padded_len;
|
|
167
168
|
let data_pixels = (data_with_markers_len + 2) / 3;
|
|
168
169
|
let end_marker_pixels = 3;
|
|
@@ -174,7 +175,7 @@ fn write_png_from_zst(
|
|
|
174
175
|
let height = side;
|
|
175
176
|
let row_bytes = width * 3;
|
|
176
177
|
let total_data_bytes = width * height * 3;
|
|
177
|
-
let marker_end_pos =
|
|
178
|
+
let marker_end_pos = total_data_bytes - marker_end_bytes;
|
|
178
179
|
|
|
179
180
|
let enc_header_bytes = if let Some(ref enc) = encryptor {
|
|
180
181
|
enc.header.clone()
|
package/package.json
CHANGED
|
Binary file
|