roxify 1.7.6 → 1.8.1

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/native/gpu.rs ADDED
@@ -0,0 +1,116 @@
1
+ use anyhow::{anyhow, Result};
2
+ use parking_lot::RwLock;
3
+ use std::sync::Arc;
4
+ use wgpu::*;
5
+ use wgpu::util::DeviceExt;
6
+
7
+ pub struct GpuDevice {
8
+ device: Device,
9
+ queue: Queue,
10
+ supported: bool,
11
+ adapter_info: String,
12
+ }
13
+
14
+ pub struct GpuContext {
15
+ inner: Arc<RwLock<Option<GpuDevice>>>,
16
+ }
17
+
18
+ impl GpuContext {
19
+ pub async fn new() -> Self {
20
+ let instance = Instance::new(InstanceDescriptor {
21
+ backends: Backends::all(),
22
+ ..Default::default()
23
+ });
24
+
25
+ let adapter = instance.request_adapter(&RequestAdapterOptions {
26
+ power_preference: PowerPreference::HighPerformance,
27
+ compatible_surface: None,
28
+ force_fallback_adapter: false,
29
+ }).await;
30
+
31
+ let device_info = if let Some(adapter) = adapter {
32
+ match adapter.request_device(&DeviceDescriptor {
33
+ label: Some("roxify-compute"),
34
+ required_features: Features::empty(),
35
+ required_limits: Limits::default(),
36
+ }, None).await {
37
+ Ok((device, queue)) => {
38
+ let info = adapter.get_info();
39
+ Some(GpuDevice {
40
+ device,
41
+ queue,
42
+ supported: true,
43
+ adapter_info: format!("{:?}", info.driver),
44
+ })
45
+ }
46
+ Err(_) => None,
47
+ }
48
+ } else {
49
+ None
50
+ };
51
+
52
+ GpuContext {
53
+ inner: Arc::new(RwLock::new(device_info)),
54
+ }
55
+ }
56
+
57
+ pub fn is_available(&self) -> bool {
58
+ self.inner.read().is_some()
59
+ }
60
+
61
+ pub fn get_adapter_info(&self) -> Option<String> {
62
+ self.inner.read().as_ref().map(|d| d.adapter_info.clone())
63
+ }
64
+
65
+ pub async fn create_compute_pipeline(
66
+ &self,
67
+ shader_src: &str,
68
+ entry_point: &str,
69
+ ) -> Result<ComputePipeline> {
70
+ let gpu = self.inner.read();
71
+ let gpu = gpu.as_ref().ok_or_else(|| anyhow!("No GPU device available"))?;
72
+
73
+ let shader_module = gpu.device.create_shader_module(ShaderModuleDescriptor {
74
+ label: Some("compute-shader"),
75
+ source: ShaderSource::Wgsl(std::borrow::Cow::Borrowed(shader_src)),
76
+ });
77
+
78
+ let pipeline_layout = gpu.device.create_pipeline_layout(&PipelineLayoutDescriptor {
79
+ label: Some("compute-layout"),
80
+ bind_group_layouts: &[],
81
+ push_constant_ranges: &[],
82
+ });
83
+
84
+ Ok(gpu.device.create_compute_pipeline(&ComputePipelineDescriptor {
85
+ label: Some("compute-pipeline"),
86
+ layout: Some(&pipeline_layout),
87
+ module: &shader_module,
88
+ entry_point,
89
+ }))
90
+ }
91
+
92
+ pub fn create_buffer_init(&self, data: &[u8], usage: BufferUsages) -> Result<Buffer> {
93
+ let gpu = self.inner.read();
94
+ let gpu = gpu.as_ref().ok_or_else(|| anyhow!("No GPU device available"))?;
95
+
96
+ Ok(gpu.device.create_buffer_init(&util::BufferInitDescriptor {
97
+ label: None,
98
+ contents: data,
99
+ usage,
100
+ }))
101
+ }
102
+ }
103
+
104
+ pub fn gpu_available() -> bool {
105
+ pollster::block_on(async {
106
+ let instance = Instance::new(InstanceDescriptor {
107
+ backends: Backends::all(),
108
+ ..Default::default()
109
+ });
110
+ instance.request_adapter(&RequestAdapterOptions {
111
+ power_preference: PowerPreference::HighPerformance,
112
+ compatible_surface: None,
113
+ force_fallback_adapter: false,
114
+ }).await.is_some()
115
+ })
116
+ }
@@ -0,0 +1,162 @@
1
+ use anyhow::Result;
2
+ use rayon::prelude::*;
3
+ use crate::bwt::bwt_encode;
4
+ use crate::context_mixing::analyze_entropy;
5
+ use crate::rans::{build_symbols_from_frequencies, RansEncoder};
6
+ use crate::pool::BufferPool;
7
+ use std::sync::Arc;
8
+
9
+ const BLOCK_SIZE: usize = 16 * 1024 * 1024;
10
+
11
+ #[derive(Clone, Debug)]
12
+ pub struct CompressionStats {
13
+ pub original_size: u64,
14
+ pub compressed_size: u64,
15
+ pub ratio: f64,
16
+ pub entropy_bits: f32,
17
+ pub blocks_count: usize,
18
+ }
19
+
20
+ pub struct HybridCompressor {
21
+ pool: Arc<BufferPool>,
22
+ enable_gpu: bool,
23
+ block_size: usize,
24
+ }
25
+
26
+ impl HybridCompressor {
27
+ pub fn new(enable_gpu: bool, pool_size: usize) -> Self {
28
+ HybridCompressor {
29
+ pool: Arc::new(BufferPool::new(pool_size, BLOCK_SIZE)),
30
+ enable_gpu,
31
+ block_size: BLOCK_SIZE,
32
+ }
33
+ }
34
+
35
+ pub fn compress(&self, data: &[u8]) -> Result<(Vec<u8>, CompressionStats)> {
36
+ let original_size = data.len() as u64;
37
+ let entropy = analyze_entropy(data);
38
+
39
+ let blocks: Vec<&[u8]> = data.chunks(self.block_size).collect();
40
+ let blocks_count = blocks.len();
41
+
42
+ let compressed_blocks: Vec<Vec<u8>> = blocks
43
+ .into_par_iter()
44
+ .map(|block| self.compress_block(block))
45
+ .collect::<Result<Vec<_>, _>>()?;
46
+
47
+ let mut result = Vec::with_capacity(original_size as usize / 2);
48
+ result.extend_from_slice(&(blocks_count as u32).to_le_bytes());
49
+
50
+ for block in &compressed_blocks {
51
+ result.extend_from_slice(&(block.len() as u32).to_le_bytes());
52
+ result.extend_from_slice(block);
53
+ }
54
+
55
+ let compressed_size = result.len() as u64;
56
+ let ratio = (compressed_size as f64) / (original_size as f64);
57
+
58
+ Ok((result, CompressionStats {
59
+ original_size,
60
+ compressed_size,
61
+ ratio,
62
+ entropy_bits: entropy,
63
+ blocks_count,
64
+ }))
65
+ }
66
+
67
+ fn compress_block(&self, block: &[u8]) -> Result<Vec<u8>> {
68
+ if block.is_empty() {
69
+ return Ok(Vec::new());
70
+ }
71
+
72
+ let bwt = bwt_encode(block)?;
73
+ let bwt_data = &bwt.transformed;
74
+
75
+ let mut freqs = vec![0u32; 256];
76
+ for &byte in bwt_data {
77
+ freqs[byte as usize] += 1;
78
+ }
79
+
80
+ let symbols = build_symbols_from_frequencies(&freqs);
81
+ let mut encoder = RansEncoder::new(symbols);
82
+
83
+ for &byte in bwt_data {
84
+ for bit_idx in 0..8 {
85
+ let bit = (byte >> bit_idx) & 1 == 1;
86
+ let symbol_idx = if bit { 1 } else { 0 };
87
+ let _ = encoder.encode(symbol_idx);
88
+ }
89
+ }
90
+
91
+ let encoded = encoder.finish();
92
+ let mut result = Vec::with_capacity(4 + encoded.len());
93
+ result.extend_from_slice(&bwt.primary_index.to_le_bytes());
94
+ result.extend_from_slice(&encoded);
95
+
96
+ Ok(result)
97
+ }
98
+
99
+ pub fn decompress(&self, data: &[u8]) -> Result<Vec<u8>> {
100
+ if data.len() < 4 {
101
+ return Err(anyhow::anyhow!("Invalid compressed data"));
102
+ }
103
+
104
+ let blocks_count = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
105
+ let mut pos = 4;
106
+ let mut result = Vec::new();
107
+
108
+ for _ in 0..blocks_count {
109
+ if pos + 4 > data.len() {
110
+ return Err(anyhow::anyhow!("Truncated block header"));
111
+ }
112
+
113
+ let block_size = u32::from_le_bytes([
114
+ data[pos],
115
+ data[pos + 1],
116
+ data[pos + 2],
117
+ data[pos + 3],
118
+ ]) as usize;
119
+ pos += 4;
120
+
121
+ if pos + block_size > data.len() {
122
+ return Err(anyhow::anyhow!("Truncated block data"));
123
+ }
124
+
125
+ let block_data = &data[pos..pos + block_size];
126
+ pos += block_size;
127
+
128
+ let decompressed = self.decompress_block(block_data)?;
129
+ result.extend_from_slice(&decompressed);
130
+ }
131
+
132
+ Ok(result)
133
+ }
134
+
135
+ fn decompress_block(&self, block: &[u8]) -> Result<Vec<u8>> {
136
+ if block.len() < 4 {
137
+ return Ok(Vec::new());
138
+ }
139
+
140
+ let _primary_index = u32::from_le_bytes([block[0], block[1], block[2], block[3]]);
141
+ let _encoded_data = &block[4..];
142
+
143
+ Ok(Vec::new())
144
+ }
145
+
146
+ pub fn estimate_gain(&self, data: &[u8]) -> f64 {
147
+ let entropy = analyze_entropy(data);
148
+ let theoretical_min = (data.len() as f64) * (entropy as f64) / 8.0;
149
+ let ratio = theoretical_min / (data.len() as f64);
150
+ (1.0 - ratio) * 100.0
151
+ }
152
+ }
153
+
154
+ pub fn compress_high_performance(data: &[u8]) -> Result<(Vec<u8>, CompressionStats)> {
155
+ let compressor = HybridCompressor::new(true, 4);
156
+ compressor.compress(data)
157
+ }
158
+
159
+ pub fn decompress_high_performance(data: &[u8]) -> Result<Vec<u8>> {
160
+ let compressor = HybridCompressor::new(true, 4);
161
+ compressor.decompress(data)
162
+ }
@@ -0,0 +1,77 @@
1
+ use image::{ImageFormat, DynamicImage};
2
+ use std::io::Cursor;
3
+
4
+ pub fn sharp_resize(
5
+ input: &[u8],
6
+ width: u32,
7
+ height: u32,
8
+ kernel: &str,
9
+ ) -> Result<Vec<u8>, String> {
10
+ let img = image::load_from_memory(input)
11
+ .map_err(|e| format!("Failed to load image: {}", e))?;
12
+
13
+ let filter = match kernel {
14
+ "nearest" => image::imageops::FilterType::Nearest,
15
+ "bilinear" | "linear" => image::imageops::FilterType::Triangle,
16
+ "cubic" | "bicubic" => image::imageops::FilterType::CatmullRom,
17
+ "lanczos" | "lanczos3" => image::imageops::FilterType::Lanczos3,
18
+ _ => image::imageops::FilterType::Nearest,
19
+ };
20
+
21
+ let resized = img.resize_exact(width, height, filter);
22
+
23
+ let mut output = Vec::new();
24
+ resized.write_to(&mut Cursor::new(&mut output), ImageFormat::Png)
25
+ .map_err(|e| format!("Failed to encode PNG: {}", e))?;
26
+
27
+ Ok(output)
28
+ }
29
+
30
+ pub fn sharp_raw_pixels(input: &[u8]) -> Result<(Vec<u8>, u32, u32), String> {
31
+ let img = image::load_from_memory(input)
32
+ .map_err(|e| format!("Failed to load image: {}", e))?;
33
+
34
+ let rgb = img.to_rgb8();
35
+ let width = rgb.width();
36
+ let height = rgb.height();
37
+ let raw = rgb.into_raw();
38
+
39
+ Ok((raw, width, height))
40
+ }
41
+
42
+ pub fn sharp_metadata(input: &[u8]) -> Result<(u32, u32, String), String> {
43
+ let img = image::load_from_memory(input)
44
+ .map_err(|e| format!("Failed to load image: {}", e))?;
45
+
46
+ let width = img.width();
47
+ let height = img.height();
48
+ let format = match img {
49
+ DynamicImage::ImageLuma8(_) => "gray",
50
+ DynamicImage::ImageRgb8(_) => "rgb",
51
+ DynamicImage::ImageRgba8(_) => "rgba",
52
+ _ => "unknown",
53
+ };
54
+
55
+ Ok((width, height, format.to_string()))
56
+ }
57
+
58
+ pub fn rgb_to_png(rgb: &[u8], width: u32, height: u32) -> Result<Vec<u8>, String> {
59
+ use image::codecs::png::{PngEncoder, CompressionType, FilterType};
60
+ use image::ImageEncoder;
61
+
62
+ let mut output = Vec::new();
63
+ // Data is already zstd-compressed, so PNG deflate adds overhead.
64
+ // Use Uncompressed (stored blocks) to avoid wasting CPU on incompressible data.
65
+ let encoder = PngEncoder::new_with_quality(
66
+ &mut output,
67
+ CompressionType::Uncompressed,
68
+ FilterType::NoFilter,
69
+ );
70
+ encoder.write_image(rgb, width, height, image::ExtendedColorType::Rgb8)
71
+ .map_err(|e| format!("Failed to encode PNG: {}", e))?;
72
+ Ok(output)
73
+ }
74
+
75
+ pub fn png_to_rgb(png: &[u8]) -> Result<(Vec<u8>, u32, u32), String> {
76
+ sharp_raw_pixels(png)
77
+ }