Files
UnionApp/lib/service/steganography_util.dart
spasolreisa b985cd1f9e ver1.00.00
bindQR
fix
2026-04-19 23:31:52 +08:00

104 lines
3.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'dart:typed_data';
import 'package:image/image.dart' as img;
class SteganographyUtil {
static const String MAGIC_HEADER = 'SGWCMAID';
/// 图片隐写:写入数据
static Uint8List hideDataInImage(Uint8List imageBytes, String secretData) {
// 1. 解码图片
final image = img.decodeImage(imageBytes);
if (image == null) throw Exception('图片解码失败');
// 2. 准备数据:必须以 MAGIC_HEADER 开头,并以 \0 结尾
final dataBytes = utf8.encode(secretData);
final payload = Uint8List(dataBytes.length + 1);
payload.setRange(0, dataBytes.length, dataBytes);
payload[dataBytes.length] = 0; // 终止符
// 检查容量
if (payload.length * 8 > image.width * image.height) {
throw Exception('图片像素不足以容纳数据');
}
int payloadIndex = 0;
int bitIndex = 7; // MSB First (最高位优先)
// ✅ 修复:必须严格按照 Java 的 (y, x) 顺序遍历,确保坐标对齐
bool finished = false;
for (int y = 0; y < image.height; y++) {
for (int x = 0; x < image.width; x++) {
if (payloadIndex >= payload.length) {
finished = true;
break;
}
// 获取当前坐标的像素
final pixel = image.getPixel(x, y);
// 取出当前字节的特定位
int currentByte = payload[payloadIndex];
int bit = (currentByte >> bitIndex) & 1;
// ✅ 修复:通过 red 分量操作屏蔽不同图片格式RGBA/BGRA的底层差异
int r = pixel.r.toInt();
int g = pixel.g.toInt();
int b = pixel.b.toInt();
int a = pixel.a.toInt();
// 修改 R 通道最低位
r = (r & 0xFE) | bit;
// 写回像素(保持其他通道不变)
image.setPixel(x, y, img.ColorRgba8(r, g, b, a));
bitIndex--;
if (bitIndex < 0) {
bitIndex = 7;
payloadIndex++;
}
}
if (finished) break;
}
// ✅ 必须导出为 PNG防止 JPEG 压缩破坏 LSB 位
return Uint8List.fromList(img.encodePng(image));
}
/// 提取数据(本地调试用,逻辑需与写入完全对称)
static String? extractDataFromImage(Uint8List imageBytes) {
final image = img.decodeImage(imageBytes);
if (image == null) return null;
List<int> extractedBytes = [];
int currentByte = 0;
int bitCount = 0;
outer:
for (int y = 0; y < image.height; y++) {
for (int x = 0; x < image.width; x++) {
final pixel = image.getPixel(x, y);
int bit = pixel.r.toInt() & 1;
// MSB First 组装字节
currentByte = (currentByte << 1) | bit;
bitCount++;
if (bitCount == 8) {
if (currentByte == 0) break outer; // 结束符
extractedBytes.add(currentByte);
currentByte = 0;
bitCount = 0;
}
}
}
if (extractedBytes.isEmpty) return null;
try {
return utf8.decode(extractedBytes);
} catch (e) {
return null;
}
}
}