// pages/piano/cropper/cropper.js const { BtCmd } = require('../../../devices/bluetooth/bt_cmd'); import EventManager from '../../../utils/event_bus' import { EnumCmdEvent, CmdEvent } from '../../../devices/cmd_key_event'; import route_util from '../../../utils/route_util'; const { BtHelper } = require('../../../devices/bt_helper'); Page({ /** * 页面的初始数据 */ data: { navbarData: { showCapsule: 1, //是否显示左上角图标 1表示显示 0表示不显示 title: '壁纸设置', //导航栏 中间的标题 }, src: "", width: 320,//宽度 height: 320,//高度 showProgress: false, _chunks: [], progress: 0, _timer: null, _imgUrl: null, }, cropper: null, callback() { let that = this if (this.data._chunks.length > 0) { wx.showModal({ title: '保存壁纸中,确定要中断退出吗?', content: '', complete: (res) => { if (res.confirm) { that.endImage(2) wx.navigateBack({ delta: 1, }) } } }) } else { wx.navigateBack({ delta: 1, }) } }, convertImageDataToRGB565(imageData, width, height) { // 创建一个 Uint16Array 来存储 RGB565 数据 const rgb565Data = new Uint16Array(width * height); // 将 RGBA 数据转换为 RGB565 数据 for (let i = 0; i < imageData.length; i += 4) { const r = imageData[i]; const g = imageData[i + 1]; const b = imageData[i + 2]; // const a = imageData[i + 3]; // 透明度,如果需要可以使用 // 将 RGB 转换为 RGB565 const rgb565 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); rgb565Data[i / 4] = rgb565; } return rgb565Data }, generateLVGLBinFile(rgb565Data, width, height) { const header = new ArrayBuffer(32); const headerView = new DataView(header); // 十六进制字符串 // const hexString = "04 80 07 3c ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff"; const hexString = "04 80 07 3c"; // 将十六进制字符串转换为字节数组 const bytes = hexString.split(' ').map(byte => parseInt(byte, 10)); console.log(bytes); // 将字节数组写入 headerView bytes.forEach((byte, index) => { headerView[index] = byte }); // 创建文件数据 const fileData = new Uint8Array(header.byteLength + rgb565Data.byteLength); console.log("fileData:", fileData.length, fileData.byteLength); fileData.set(new Uint8Array(header), 0); // 添加头部信息 fileData.set(new Uint8Array(rgb565Data.buffer), header.byteLength); // 添加 RGB565 数据 return fileData; }, // 转换 RGBA 数据为 RGB565 imageDataToRGB565(rgbaData, width, height) { const pixelCount = width * height; // 十六进制字符串 const hexString = "04 80 07 3c"; // 将十六进制字符串转换为字节数组 // const bytes = hexString.split(' ').map(byte => parseInt(byte, 10)); const bytes = ["04", "80", "07", "3c"]; console.log(bytes); const outputData = new Uint8Array(pixelCount * 2 + bytes.length); // 文件头 + 每像素 4 字节 (RGB888 + Alpha) // 将字节数组写入 headerView bytes.forEach((byte, index) => { outputData[index] = parseInt(byte, 16) }); // 写入像素数据 let offset = bytes.length; for (let i = 0; i < pixelCount; i++) { const r = rgbaData[i * 4]; const g = rgbaData[i * 4 + 1]; const b = rgbaData[i * 4 + 2]; // 转换为 RGB565 格式 const r5 = (r >> 3) & 0x1F; const g6 = (g >> 2) & 0x3F; const b5 = (b >> 3) & 0x1F; const rgb565 = (r5 << 11) | (g6 << 5) | b5; // 写入 RGB565 (小端序) outputData[offset++] = rgb565 & 0xFF; // 低字节 outputData[offset++] = (rgb565 >> 8) & 0xFF; // 高字节 } console.log('最终偏移:', offset) return outputData; }, cropperload(e) { console.log('cropper加载完成'); }, upload() { let that = this; wx.chooseMedia({ count: 1, mediaType: ['image'], sourceType: ['album'], success(res) { wx.showLoading({ title: '加载中', }) const tempFilePaths = res.tempFiles[0].tempFilePath; // //重置壁纸角度、缩放、位置 that.cropper.imgReset(); that.setData({ src: tempFilePaths }); // that.testUpload(tempFilePaths) }, fail(res) { console.log(res) } }) }, testUpload(url) { const fs = wx.getFileSystemManager(); wx.showLoading({ title: '壁纸裁剪中', }) let _this = this // app.globalData.imgSrc = obj.url; console.log("裁剪壁纸:", url); _this.data._imgUrl = url fs.readFile({ filePath: url, encoding: '', // 不指定编码以获取原始二进制数据 success: (obj) => { console.log("加载文件成功:", obj.data.byteLength, obj.data.length) let binData = obj.data; _this.sliceDataIntoChunks(binData, 64); console.log("加载文件成功2:", binData.byteLength, binData.length) _this.data._imageBufferLength = binData.byteLength ?? binData.length wx.hideLoading(); wx.showLoading({ title: '开始传输壁纸', }) _this.startImage() }, fail: (err) => { console.error('读取 .bin 文件失败:', err); }, }); }, loadimage(e) { wx.hideLoading(); console.log('壁纸', e); this.cropper.imgReset(); }, clickcut(e) { console.log("clickcut:", e.detail); //壁纸预览 wx.previewImage({ current: e.detail.url, urls: [e.detail.url] }) }, cancel() { wx.navigateBack({ delta: -1 }) }, submit() { let _this = this // this.cropper.getImgData((obj) => { // _this.cropper.getImg((obj2) => { // // app.globalData.imgSrc = obj.url; // console.log("裁剪壁纸:", obj2); // _this.data._imgUrl = obj2.url // }); // wx.showLoading({ // title: '壁纸裁剪中', // }) // // let binData = _this.imageDataToRGB565(obj.data, obj.width, obj.height) // // const rgb565Data = _this.convertImageDataToRGB565(obj.data, obj.width, obj.height) // // // 生成 LVGL 二进制文件 // // const binData = _this.generateLVGLBinFile(rgb565Data, obj.width, obj.height); // const binData = _this.imageDataToRGB565(obj.data, obj.width, obj.height); // _this.sliceDataIntoChunks(binData, 128); // console.log("下载文件成功2:", binData.byteLength, binData.length) // _this.data._imageBufferLength = binData.byteLength ?? binData.length // wx.hideLoading(); // wx.showLoading({ // title: '开始传输壁纸', // }) // _this.startImage(); // }); // 读取裁剪的jpg图片 const fs = wx.getFileSystemManager(); this.cropper.getImg((obj) => { wx.showLoading({ title: '壁纸裁剪中', }) // app.globalData.imgSrc = obj.url; console.log("裁剪壁纸:", obj); _this.data._imgUrl = obj.url fs.readFile({ filePath: obj.url, encoding: '', // 不指定编码以获取原始二进制数据 success: (obj) => { console.log("加载文件成功:", obj.data.byteLength, obj.data.length) let binData = obj.data; _this.sliceDataIntoChunks(binData, 64); console.log("加载文件成功2:", binData.byteLength, binData.length) _this.data._imageBufferLength = binData.byteLength ?? binData.length wx.hideLoading(); wx.showLoading({ title: '开始传输壁纸', }) _this.startImage() }, fail: (err) => { console.error('读取 .bin 文件失败:', err); }, }); }); // 651kb的 // _this.downloadAndSaveFile("https://music-play.oss-cn-shenzhen.aliyuncs.com/backOss/file/8770a6097d9940b59032d099b2fdde3b.bin"); }, // 下载网络文件并保存到本地 downloadAndSaveFile(url) { const fs = wx.getFileSystemManager(); let _this = this; wx.downloadFile({ url: url, // 网络文件的 URL success: (res) => { if (res.statusCode === 200) { // 下载成功,res.tempFilePath 是临时文件路径 const tempFilePath = res.tempFilePath; console.log("下载文件:", res) fs.readFile({ filePath: tempFilePath, // encoding: '', // 不指定编码以获取原始二进制数据 success: (res) => { console.log("下载文件成功:", res.data) let rgbData = res.data _this.data._imageBufferLength = rgbData.byteLength wx.hideLoading(); wx.showLoading({ title: '开始传输壁纸', }) _this.startImage(rgbData); }, fail: (err) => { console.error('读取 .bin 文件失败:', err); }, }); } else { console.error('下载文件失败:', res.statusCode); } }, fail: (err) => { console.error('下载文件失败:', err); }, }); }, checkAndRequestImagePermission: function () { const _this = this; // 检查用户是否已经授权访问相册 wx.getSetting({ success(res) { if (!res.authSetting['scope.writePhotosAlbum']) { // 用户未授权访问相册,请求用户授权 wx.authorize({ scope: 'scope.writePhotosAlbum', success() { // 用户同意授权 console.log('用户已授权访问相册1'); _this.upload() // 可以在这里执行访问相册的操作 }, fail() { // 用户拒绝授权 console.log('用户拒绝授权访问相册'); wx.showModal({ title: '提示', content: '您拒绝了访问相册的权限,无法上传壁纸', showCancel: false, success(res) { if (res.confirm) { // 跳转到设置页面 wx.openSetting({ success(settingRes) { if (settingRes.authSetting['scope.writePhotosAlbum']) { console.log('用户已在设置中开启访问相册的权限'); // 可以在这里执行访问相册的操作 _this.upload(); //上传壁纸 } else { console.log('用户仍未授权访问相册'); } } }); } } }); } }); } else { // 用户已授权访问相册 console.log('用户已授权访问相册2'); // 可以在这里执行访问相册的操作 } } }); }, async startImage() { BtHelper.sendCallBack(BtCmd.wallPaper(1), function (res) { if (!res) { wx.hideLoading() wx.showToast({ title: '发送失败', icon: 'none' }) } }); }, sliceDataIntoChunks(data, chunkSize) { let buffer = data; console.log("发送壁纸数据:", buffer.byteLength, buffer.length) let total = buffer.byteLength || buffer.length const chunks = []; // for (let i = 0; i < data.length; i += chunkSize) { for (let i = 0; i < total; i += chunkSize) { const chunk = buffer.slice(i, i + chunkSize); chunks.push(chunk); } // const sliceSize = chunkSize / 2; // 每个 RGB565 占 2 字节 // const chunks = []; // for (let i = 0; i < total; i += sliceSize) { // chunks.push(buffer.slice(i, i + sliceSize)); // } this.data._chunks = chunks; }, async sendImage() { let _this = this wx.hideLoading() let chunks = _this.data._chunks let next = 0; let total = _this.data._imageBufferLength; let btHelper = BtHelper.getInstance(); let i = 0; _this.data._timer = setInterval(async () => { if (i > chunks.length - 1) { wx.showModal({ title: '壁纸上传成功:' + i + "总:" + chunks.length, showCancel: false, success(res) { if (res.confirm) { const pages = getCurrentPages(); // 获取上一级页面实例 const prevPage = pages[pages.length - 2]; // 传递参数 prevPage.setData({ topImg: { "pic": _this.data._imgUrl, } }); wx.navigateBack({ delta: 1, }) } } }) _this.endImage(0) return; } const chunk = chunks[i]; next += (chunk.byteLength ?? chunk.length); // let uint8Array = new Uint8Array(chunk); // let unit16 = uint8Array.map(item => { // return item.toString(16) // }); console.log("发送壁纸数据1:", i, ":", next, ":", chunk.length, ":", chunk.byteLength, ":", total, chunks.length) let res = await btHelper.wallPaperSyncData(chunk); // let res = true; // btHelper.wallPaperData(chunk); if (!res) { wx.showModal({ title: '壁纸上传失败', showCancel: false }) _this.endImage(2) } _this.updateProgress(next, total); i++; }, 30); }, async delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }, endImage(value) { let _this = this // console.log("结束壁纸上传:", value) if (_this.data._timer) { clearInterval(_this.data._timer) _this.data._timer = null } _this.setData({ progress: 0, showProgress: false }) _this.data._chunks = [] BtHelper.getInstance().wallPaper(value); }, startProgress() { this.setData({ progress: 0, showProgress: true }) }, updateProgress(chunk, total) { let progress = parseFloat((chunk / total * 100).toFixed(2)); let _this = this if (chunk >= total) { _this.setData({ progress: 100, showCropImg: false }); } else { _this.setData({ progress: progress, }); } }, /** * 生命周期函数--监听页面加载 */ onLoad(options) { this.checkAndRequestImagePermission() this.cropper = this.selectComponent("#image-cropper"); this.cropper.imgReset(); // this.setData({ // scr:json, // }) this.upload(); //上传壁纸 let _this = this; EventManager.addNotification(CmdEvent.eventName, function (event) { let name = event.cmdEvent; console.log("裁剪页:", EnumCmdEvent) switch (name) { case EnumCmdEvent.wallpaper: let otaCmd = event.wallpaper; let kind = event.heiJiaoKind; console.log("裁剪页:", otaCmd, kind) if (otaCmd === 1 && kind == 1) { // 开始发送 _this.sendImage() _this.startProgress() } else if (otaCmd === 0 && kind == 1) { _this.setData({ progress: 0, showCropImg: false }); // 发送结束 } else if (kind == 0) { wx.hideLoading() wx.showModal({ title: '壁纸上传失败了', showCancel: false }) _this.setData({ progress: 0, showCropImg: false }); if (_this.data._timer) { clearInterval(_this.data._timer) _this.data._timer = null } } break; default: break; } }, this) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, /** * 生命周期函数--监听页面隐藏 */ onHide() { }, /** * 生命周期函数--监听页面卸载 */ onUnload() { EventManager.removeNotification(CmdEvent.eventName, this); }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom() { }, /** * 用户点击右上角分享 */ onShareAppMessage() { } })