import 'dart:convert'; import 'dart:typed_data'; import 'package:image/image.dart' as img; import '../tool/encryption_util.dart'; class SteganographyUtil { static const String MAGIC_HEADER = 'SGWCMAID'; /// 图片隐写:将数据藏进图片 R 通道最低位 static Uint8List hideDataInImage(Uint8List imageBytes, String secretData) { if (!secretData.startsWith(MAGIC_HEADER)) { throw Exception('数据格式不正确,必须以 $MAGIC_HEADER 开头'); } // 1. 加密 + 压缩 final encryptedString = EncryptionUtil.encryptAndCompress(secretData); // 2. 转为字节并添加 \0 结束符 final dataBytes = utf8.encode(encryptedString); final payload = Uint8List(dataBytes.length + 1); payload.setRange(0, dataBytes.length, dataBytes); payload[dataBytes.length] = 0; // \0 // 3. 解码图片 final image = img.decodeImage(imageBytes); if (image == null) throw Exception('图片解码失败'); // 4. 获取像素数据 final imageData = image.data; if (imageData == null) throw Exception('图片数据为空'); // 【关键修复】ByteBuffer 必须转换为 Uint8List 才能进行 [] 操作和获取 length // 注意:asUint8List() 返回的是原 buffer 的视图,修改它会直接修改 image 数据 final Uint8List pixels = imageData.buffer.asUint8List(); // 检查容量 (R通道只有 width * height 个字节可用) // pixels.length 是总字节数 (width * height * 4) final maxBits = pixels.length ~/ 4; if (payload.length * 8 > maxBits) { throw Exception('图片容量不足!需要 ${payload.length * 8} bits,图片仅提供 ${maxBits} bits'); } // 5. 执行 LSB 隐写 _writeLsbRChannel(pixels, payload); // 6. 导出 PNG return img.encodePng(image); } /// 从图片中提取数据 static String? extractDataFromImage(Uint8List imageBytes) { final image = img.decodeImage(imageBytes); if (image == null) return null; final imageData = image.data; if (imageData == null) return null; // 【关键修复】转换为 Uint8List final Uint8List pixels = imageData.buffer.asUint8List(); final List extractedBytes = []; int currentByteValue = 0; int bitCount = 0; // 遍历像素 (RGBA, 步长 4) for (int i = 0; i < pixels.length; i += 4) { // 只取 R 通道 (index i) 的最低位 int bit = pixels[i] & 1; currentByteValue = (currentByteValue << 1) | bit; bitCount++; if (bitCount == 8) { if (currentByteValue == 0) { break; // 遇到 \0 停止 } extractedBytes.add(currentByteValue); currentByteValue = 0; bitCount = 0; } } if (extractedBytes.isEmpty) return null; try { return utf8.decode(extractedBytes); } catch (e) { return null; } } /// 核心写入:仅修改 R 通道 static void _writeLsbRChannel(Uint8List pixels, Uint8List payload) { int payloadIndex = 0; int bitIndex = 0; // 0-7 for (int i = 0; i < pixels.length; i += 4) { if (payloadIndex >= payload.length) break; int currentByte = payload[payloadIndex]; // 取当前字节的第 bitIndex 位 (从高位到低位) int bit = (currentByte >> (7 - bitIndex)) & 1; // 修改 R 通道: 清除最低位,写入新位 pixels[i] = (pixels[i] & 0xFE) | bit; bitIndex++; if (bitIndex > 7) { bitIndex = 0; payloadIndex++; } } } }