OpenHarmony轻松玩转GIF数据渲染

OpenAtom OpenHarmony(以下简称“OpenHarmony”)提供了Image组件支持GIF动图的播放,但是缺乏扩展能力,不支持播放控制等。今天介绍一款三方库——ohos-gif-drawable三方组件,带大家一起玩转GIF的数据渲染,搞定GIF动图的各种需求。

效果演示

本文将从5个小节来带领大家使用ohos-gif-drawable这一款三方库,其中1、2、3这3个小节,主要介绍了ohos-gif-drawable的核心能力、GIF软解码和GIF绘制。4和5小节主要是扩展讨论,如何添加滤镜效果和软解码遇到的耗时问题。

1.GIF的文件格式理论基础

工欲善其事必先利其器。首先我们需要为自己打下理论基础。了解GIF的数据格式,为后续解码GIF提供理论支持。

通过学习GIF的文件格式,我们对于GIF的组成格式有了一定的了解,并且有助于理解后面GIF的解码。

在开始介绍之前,我想让大家了解一下整体的结构思路如下图:

其中gifuct-js三方库主要完成了解码的工作。

ohos-gif-drawable三方库则是在gifuct-js的三方库之上,进行了封装。并结合了OpenHarmony的Canvas绘制能力,达到了播放和控制GIF的能力。

2.GIF软解码:gifuct-js三方库介绍

GIF解码我们使用了gifuct-js这个库,它是一个纯JavaScript的GIF解码库。首先我们需要了解基础用法。

2.1 参考样例将一个文件ArrayBuffer转换为GIF解码后的帧数据数组。

//javascript
var gif = parseGIF(arraybuffer)
var frames = decompressFrames(gif, true)

2.2 由于OpenHarmony的Image生成PixelMap需要的数据是BGRA数据,而2.1生成的frames所有数组中的patch字段则是RGBA数据,所以我们需要使用

//javascript
var gif = parseGIF(arraybuffer)
var frames = decompressFrames(gif, false)

然后将frame目前还未生成的patch字段数据,通过generatePatch 函数,将RGBA的数据更换为BGRA即可,如下代码所示:

//javascript
const generatePatch = image => {const totalPixels = image.pixels.lengthconst patchData = new Uint8ClampedArray(totalPixels * 4)for (var i = 0; i < totalPixels; i++) {const pos = i * 4const colorIndex = image.pixels[i]const color = image.colorTable[colorIndex] || [0, 0, 0]patchData[pos] = color[2] // BpatchData[pos + 1] = color[1]// GpatchData[pos + 2] = color[0] // RpatchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0//A}return patchData
}

generatePatch函数,在这里会根据颜色表colorTable和基于颜色表的图像数据pixels以及透明度transparentIndex生成BGRA格式的patchData,这个数据和Canvas中getImageData获取的ImageData数据是一致的,都是Uint8ClampedArray类型,可以直接使用putImageData让canvas绘制。

最后,生成的patchData赋值给Frame的patch字段。

这里我们并没有直接使用Canvas的putImageData直接绘制。为了提升扩展性,我们使用了Image的能力来生成PixelMap,这样处理为后续滤镜效果提供了可能,也方便后续绘制流程。

好了,到这里我们就基本上把gifuct-js库的基础使用简单介绍完了。

如何使用GIF:ohos-gif-drawable三方库的介绍。

我们先来看看整个ohos-gif-drawable组件的模型图,通过模型图,我们可以看到,用户只要关注GIFComponent组件,和GIFComponent.ControllerOptions配置参数以及控制参数autoPlay和resetGif即可,非常简单!

  1. 支持的功能列表如下

● 支持播放GIF图片。
● 支持控制GIF播放/暂停。
● 支持重置GIF播放动画。
● 支持调节GIF播放速率。
● 支持监听GIF所有帧显示完成后的回调。
● 支持设置显示大小。
● 支持7种不同的展示类型。
● 支持设置显示区域背景颜色。

  1. 如何使用ohos-gif-drawable

首先需要使用npm下载ohos-gif-drawable三方库

npm install @ohos/ohos-gif-drawable --save

接下来我们需要配置一个worker给gifuct-js解码使用。

配置worker,在应用工程的entry/src/main/ets/pages目录下新建workers文件夹,并且创建文件 gifParseWorker.ts ,文件内容如下:

import arkWorker from '@ohos.worker';
import { handler } from '@ohos/ohos-gif-drawable/src/main/ets/components/gif/worker/GifWorker'
// handler封装了子线程逻辑,但worker目前只能在entry中进行创建arkWorker.parentPort.onmessage = handler;

然后在entry目录的build-profile.json5文件中,添加如下内容:

"buildOption": { 
"sourceOption": {   
"workers": [    "./src/main/ets/pages/workers/gifParseWorker.ts"
] 
}
},

到这里我们worker就配置好了。

下面就到了正式使用环节,我们只要在UI界面需要的地方写上自定义控件GIFComponent,然后传入GIFComponent.ControllerOptions,gifAutoPlay,gifReset这三个参数就能控制gif动画。

import { GIFComponent, ResourceLoader } from '@ohos/ohos-gif-drawable'
// gif绘制组件用户属性设置
@State model:GIFComponent.ControllerOptions = new GIFComponent.ControllerOptions();
// 是否自动播放
@State gifAutoPlay:boolean = true;
// 重置GIF播放,每次取反都能生效
@State gifReset:boolean = true;
// 在ARKUI的其他容器组件中添加该组件
GIFComponent({model:$model, autoPlay:$gifAutoPlay, resetGif:this.gifReset})

举个简单的例子说明一下

// 创建worker
let worker = new ArkWorker.Worker('entry/ets/pages/workers/gifParseWorker.ts', {type: 'classic',name: 'loadUrlByWorker'})
// 关闭动画     
this.gifAutoPlay = false;
// 销毁上一次资源
this.model.destroy();
// 新创建一个modelx,用于配置用户参数
let modelx = new GIFComponent.ControllerOptions()
modelx 
// 配置回调动画结束监听,和耗时监听   
.setLoopFinish((loopTime) => {  
this.gifLoopCount++;  
this.loopHint = '当前gif循环了' + this.gifLoopCount + '次,耗时=' + loopTime + 'ms'  
}) 
// 设置组件大小   
.setSize({ width: this.compWidth, height: this.compHeight }) 
// 设置图像和组件的适配类型 
.setScaleType(this.scaleType) 
// 设置播放速率 
.setSpeedFactor(this.speedFactor) 
// 设置背景 
.setBackgroundColor(Color.Grey)
// 加载网络图片,getContext(this)中的this指向page页面或者组件都可以ResourceLoader.downloadDataWithContext(getContext(this), {   url: 'https://pic.ibaotu.com/gif/18/17/16/51u888piCtqj.gif!fwpaa70/fw/700'   }, (sucBuffer) => {   
// 网络资源sucBuffer返回后处理  
modelx.loadBuffer(sucBuffer, () => {      console.log('网络加载解析成功回调绘制!')   
// 开启自动播放     
this.gifAutoPlay = true;   
// 给组件数据赋新的用户配置参数,达到后续gif动画效果     
this.model = modelx;   }, worker)}, (err) => {  
// 用户根据返回的错误信息,进行业务处理(展示一张失败占位图、再次加载一次、加载其他图片等)
})

这里ResourceLoader内置了加载网络资源GIF,本地工程资源GIF和本地路径资源GIF文件数据的能力。

如果你已经有了GIF文件的arraybuffer数据,也可以直接调用modelx.loadBuffer(buffer: ArrayBuffer, readyRender: (err?) => void, worker: any)进行GIF播放。

甚至你已经生成了GIF解析数据,比如调用了2.2中的解码代码,那么你也可以直接调用modelx.setFrames(images?: GIFFrame[])来进行gif播放。

1.控制GIF的播放与暂停:

this.gifAutoPlay = true 开启动画
this.gifAutoPlay = false 暂停动画

组件内部会监听该参数的变化,用户只要改变值即可达到控制效果

2. 重置GIF的播放

this.gifReset = !this.gifReset 每次变化都会重置gif播放。

由于重置不需要状态管理,所以组件内监听到数据变化就会重置gif播放

3. 设置GIF动画播放速度

let modelx = new GIFComponent.ControllerOptions()
modelx.setSpeedFactor(2)// 将速率提升到2倍

调用setSpeedFactor(speed: number)即可调整播放速度speed 为对比原始速率的乘积因子,比如设置0.5即为原始速率的0.5倍,设置为2即为原始速率的2倍。

4. 监听GIF动画播放回调(比如第一次动画结束)和获取动画实际播放总时长

let modelx = new GIFComponent.ControllerOptions()
modelx.setLoopFinish((loopTime?) => {
// loopTime为GIF动画一周期耗时,回调时间为GIF动画一周期结束时间节点
})

调用setLoopFinish(fn: (loopTime?) => void)可以通过回调得到GIF动画运行一周期耗时和一周期结束时间节点。

5. 显示GIF任意一帧

let modelx = new GIFComponent.ControllerOptions()
modelx.setSeekTo(5) // 直接展示该gif第5帧图像

调用setSeekTo(gifPosition: number)可以直接展示该gif的某一帧图像。

到这里ohos-gif-drawable三方库的主要能力都介绍完了,是不是很简单呢!

6. 适配组件的大小

let modelx = new GIFComponent.ControllerOptions()

modelx.setScaleType(ScaleType.FIT_CENTER) // 将图像缩放适配组件大小调用setScaleType(scaletype: ScaleType)可以将图像和组件大小进行适配。

目前支持的类型如下图所示:

GIFComponent.ScaleType

为什么要配置worker

在具体实践过程中我们会发现,当我们按下解码按钮的时候,主界面会有一点卡顿的情况。特别是大的GIF文件进行解码的时候效果更明显。这是因为我们在主线程中进行了CPU的密集型计算,这是一个耗时且占用CPU的操作。主线程中是不能执行耗时操作的。但是JavaScript只有一个线程啊?那么解码这一块操作该如何处理会比较好呢?带着疑惑,我去查阅了资料发现JavaScript虽然属于单线程环境。但是通过引入Worker的能力,引入子线程worker,可以实现JavaScript的“多线程”技术。

OpenHarmony如何在子线程中处理耗时任务

为了争取良好的用户体验,我们需要将耗时操作封装至子线程中。

这里简单描述一下worker的能力:

能够让主页面运行的JavaScript线程中加载运行另外单独的一个或者多个JavaScript线程,但是它的多线程编程能力区别于传统意义上的多线程编程。主线程和Worker线程之间,不会共享任何作用域和资源,他们的通信方式是基于事件监听机制的 message。

接下来我们参考OpenHarmony文档下的worker能力

1. OpenHarmony环境下Worker的API接口列表

2. Worker的使用简单案例

经过了解之后,我们可以把解码的耗时封装到worker中处理,避免主线程耗时操作占用CPU导致卡顿问题。提升用户体验。

这也是使用ohos-gif-drawable三方库需要配置worker的原因。

扩展部分

GIF的滤镜效果

1. 灰白滤镜

//javascript
// 重点代码更改 let avg = (color[0] + color[1] + color[2]) / 3patchData[pos] = avg;patchData[pos + 1] = avg;patchData[pos + 2] = avg;patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0;

2. 反转滤镜

//javascript
// 重点代码更改patchData[pos] = 255 - color[0];patchData[pos + 1] = 255 - color[1];patchData[pos + 2] = 255 - color[2];patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0;

3. 高级滤镜效果

假设我们这边已经拿到了patch: Uint8ClampedArray像素数据,这里我需要先将其变换为一张PixelMap数据,参考GIFComponent中patch数据转换为PixelMap的代码。

//typescript
import image from "@ohos.multimedia.image"
let colorBuffer = patch.buffer
let pixelmap = await image.createPixelMap(colorBuffer, {'size': {'height': frame.dims.height as number,'width': frame.dims.width as number}
})

4. 高斯模糊

然后对PixelMap像素数据进行高斯模糊, 调用 blur(pixelmap,10,true, (outPixelMap)=>{ // 模糊后的pixelmap数据})在回调中获取模糊后的pixelmap。以下是模糊处理的算法:

export async function blur(bitmap: any, radius: number, canReuseInBitmap: boolean, func: AsyncTransform<PixelMap>) {if (radius < 1) {func("error,radius must be greater than 1 ", null);return;}let imageInfo = await bitmap.getImageInfo();let size = {width: imageInfo.size.width,height: imageInfo.size.height}if (!size) {func(new Error("fastBlur The image size does not exist."), null)return;}let w = size.width;let h = size.height;var pixEntry: Array<PixelEntry> = new Array()var pix: Array<number> = new Array()let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());await bitmap.readPixelsToBuffer(bufferData);let dataArray = new Uint8Array(bufferData);for (let index = 0; index < dataArray.length; index+=4) {const r = dataArray[index];const g = dataArray[index+1];const b = dataArray[index+2];const f = dataArray[index+3];let entry = new PixelEntry();entry.a = 0;entry.b = b;entry.g = g;entry.r = r;entry.f = f;entry.pixel = ColorUtils.rgb(entry.r, entry.g, entry.b);pixEntry.push(entry);pix.push(ColorUtils.rgb(entry.r, entry.g, entry.b));}let wm = w - 1;let hm = h - 1;let wh = w * h;let div = radius + radius + 1;let r = CalculatePixelUtils.createIntArray(wh);let g = CalculatePixelUtils.createIntArray(wh);let b = CalculatePixelUtils.createIntArray(wh);let rsum, gsum, bsum, x, y, i, p, yp, yi, yw: number;let vmin = CalculatePixelUtils.createIntArray(Math.max(w, h));let divsum = (div + 1) >> 1;divsum *= divsum;let dv = CalculatePixelUtils.createIntArray(256 * divsum);for (i = 0; i < 256 * divsum; i++) {dv[i]=(i / divsum);
}yw = yi =0;let stack = CalculatePixelUtils.createInt2DArray(div,3);let stackpointer, stackstart, rbs, routsum, goutsum, boutsum, rinsum, ginsum, binsum: number;let sir: Array<number>;let r1 = radius +1;
for(y =0; y < h; y++){rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum =0;
for(i =-radius; i <= radius; i++){p = pix[yi + Math.min(wm, Math.max(i,0))];sir = stack[i + radius];sir[0]=(p &0xff0000)>>16;sir[1]=(p &0x00ff00)>>8;sir[2]=(p &0x0000ff);rbs = r1 - Math.abs(i);rsum += sir[0]* rbs;gsum += sir[1]* rbs;bsum += sir[2]* rbs;
if(i >0){rinsum += sir[0];ginsum += sir[1];binsum += sir[2];
}else{routsum += sir[0];goutsum += sir[1];boutsum += sir[2];
}
}stackpointer = radius;for(x =0; x < w; x++){r[yi]= dv[rsum];g[yi]= dv[gsum];b[yi]= dv[bsum];rsum -= routsum;gsum -= goutsum;bsum -= boutsum;stackstart = stackpointer - radius + div;sir = stack[stackstart % div];routsum -= sir[0];goutsum -= sir[1];boutsum -= sir[2];if(y ==0){vmin[x]= Math.min(x + radius +1, wm);
}p = pix[yw + vmin[x]];sir[0]=(p &0xff0000)>>16;sir[1]=(p &0x00ff00)>>8;sir[2]=(p &0x0000ff);rinsum += sir[0];ginsum += sir[1];binsum += sir[2];rsum += rinsum;gsum += ginsum;bsum += binsum;stackpointer =(stackpointer +1)% div;sir = stack[(stackpointer)% div];routsum += sir[0];goutsum += sir[1];boutsum += sir[2];rinsum -= sir[0];ginsum -= sir[1];binsum -= sir[2];yi++;
}yw += w;
}
for(x =0; x < w; x++){rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum =0;yp =-radius * w;
for(i =-radius; i <= radius; i++){yi = Math.max(0, yp)+ x;sir = stack[i + radius];sir[0]= r[yi];sir[1]= g[yi];sir[2]= b[yi];rbs = r1 - Math.abs(i);rsum += r[yi]* rbs;gsum += g[yi]* rbs;bsum += b[yi]* rbs;if(i >0){rinsum += sir[0];ginsum += sir[1];binsum += sir[2];
}else{routsum += sir[0];goutsum += sir[1];boutsum += sir[2];
}if(i < hm){yp += w;
}
}yi = x;stackpointer = radius;
for(y =0; y < h; y++){
// Preserve alpha channel: ( 0xff000000 & pix[yi] )pix[yi]=(0xff000000& pix[Math.round(yi)])|(dv[Math.round(rsum)]<<16)|(dv[Math.round(gsum)]<<8)| dv[Math.round(bsum)];rsum -= routsum;gsum -= goutsum;bsum -= boutsum;stackstart = stackpointer - radius + div;sir = stack[stackstart % div];routsum -= sir[0];goutsum -= sir[1];boutsum -= sir[2];if(x ==0){vmin[y]= Math.min(y + r1, hm)* w;
}p = x + vmin[y];sir[0]= r[p];sir[1]= g[p];sir[2]= b[p];rinsum += sir[0];ginsum += sir[1];binsum += sir[2];rsum += rinsum;gsum += ginsum;bsum += binsum;stackpointer =(stackpointer +1)% div;sir = stack[stackpointer];routsum += sir[0];goutsum += sir[1];boutsum += sir[2];rinsum -= sir[0];ginsum -= sir[1];binsum -= sir[2];yi += w;
}
}let bufferNewData =newArrayBuffer(bitmap.getPixelBytesNumber());let dataNewArray =newUint8Array(bufferNewData);let index =0;for(let i =0; i < dataNewArray.length; i +=4){dataNewArray[i]= ColorUtils.red(pix[index]);dataNewArray[i+1]= ColorUtils.green(pix[index]);dataNewArray[i+2]= ColorUtils.blue(pix[index]);dataNewArray[i+3]= pixEntry[index].f;index++;
}await bitmap.writeBufferToPixels(bufferNewData);
if(func){
func("success", bitmap);
}
}

如果需要高级滤镜效果可以参考ImageKnife组件的transform部分,这里仅仅展示模糊效果。

由于滤镜效果目前ohos-gif-drawable三方库并没有开发接口提供出来,所以开发者可以根据实际需求重写自定义组件GIFComponent.,只需要在生成PixelMap的代码片段中加入滤镜代码,即可利用滤镜效果开发更多精彩的应用。

为了帮助到大家能够更有效的学习OpenHarmony 开发的内容,下面特别准备了一些相关的参考学习资料:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14816.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在JS中克隆对象

在JavaScript中&#xff0c;克隆对象并不是直接支持的操作&#xff0c;因为JavaScript中的对象是通过引用传递的&#xff0c;而不是通过值传递。但是&#xff0c;你可以使用几种不同的方法来"克隆"或"复制"一个对象。 1. 浅拷贝&#xff08;Shallow Copy&…

二手车经营效率罗盘,用经营效率罗盘玩转二手车生意

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89292198 更多资源下载&#xff1a;关注我。 带你了解不一样的二手车圈让二手车经营更高效 课程介绍 随着时代不断发展&#xff0c;二手车行业迎来了快速变革期。二手车有着一车一况、一车一价非标和价…

无人机+飞行服务:无人机飞防服务(打药+施肥+播种)技术详解

无人机飞防服务&#xff0c;结合了先进的无人机技术与农业实践&#xff0c;为现代农业提供了高效、精准的打药、施肥和播种解决方案。以下是对这些技术的详细解析&#xff1a; 一、无人机打药技术 无人机打药技术利用无人机搭载喷雾设备&#xff0c;对农田进行精准施药。通过…

【数仓系列】maxcompute、postgresql、sparksql等行转列数据处理实战总结(其他类型持续总结更新)

1.熟悉、梳理、总结项目研发实战中的SQL开发日常使用中的问题、经验总结&#xff0c;都是常用的开发技能&#xff0c;可以省去很多时间&#xff0c;时间长就忘记了 2.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 文章目录 1.maxcompu…

关于java.nio.file 包--用于文件和文件系统操作的核心包之一

java.nio.file 包是 Java 中用于文件和文件系统操作的核心包之一。这个包提供了许多类和接口&#xff0c;用于在 Java 虚拟机中访问文件、管理文件属性和操作文件系统。主要的功能包括&#xff1a; 1.访问文件和目录: java.nio.file.Files 类提供了许多静态方法&#xff0c;用于…

【Crypto】看我回旋踢

文章目录 一、看我回旋踢二、知识点什么是ROT13&#xff1f;工作原理分析字符串格式 解题感悟 一、看我回旋踢 关键词回旋&#xff0c;盲猜ROT13 因为以 synt{ 开头&#xff0c;并以 } 结束&#xff0c;基本可以判断是ROT13 小小flag&#xff0c;拿下&#xff01; 二、知识点 …

抖音极速版:抖音轻量精简版本,新人享大福利

和快手一样&#xff0c;抖音也有自己的极速版&#xff0c;可视作抖音的轻量精简版&#xff0c;更专注于刷视频看广告赚钱&#xff0c;收益比抖音要高&#xff0c;可玩性更佳。 抖音极速版简介 抖音极速版是一个提供短视频创业和收益任务的平台&#xff0c;用户可以通过观看广…

leetcode-560 和为k的数组

一、题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 注意&#xff1a;nums中的元素可为负数 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2输入&#xff1a;num…

装饰乙级资质延期申请的注意事项

在申请装饰乙级资质延期时&#xff0c;企业需要注意以下几个重要事项&#xff1a; 提前了解政策和要求&#xff1a; 企业应提前了解并熟悉资质延期的相关政策和要求&#xff0c;包括所需材料、办理流程、时间限制等。这有助于企业做好充分准备&#xff0c;避免在申请过程中出现…

【机器学习聚类算法实战-5】机器学习聚类算法之DBSCAN聚类、K均值聚类算法、分层聚类和不同度量的聚集聚类实例分析

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

MYSQL课堂练习

学生表:studentsno&#xff0c;sname&#xff0c;ssex&#xff0c;sage&#xff0c;sdept)学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 sno为主键课程表:Course(cno&#xff0c;cname&#xff0c;) 课程号&#xff0c;课程名 cno为主键 学生选课…

申报照明工程设计乙级资质关于财务审计报告的要求

申报照明工程设计乙级资质时&#xff0c;关于财务审计报告的要求通常包括以下几点&#xff1a; 审计报告类型&#xff1a;需要提供由具有法定资质的会计师事务所出具的财务审计报告。如果是企业已经运营满一个会计年度&#xff0c;应提交最近一个完整会计年度的年度审计报告。如…

Rust:struct 与字节序列的相互转换

在 Rust 中&#xff0c;将结构体&#xff08;struct&#xff09;与字节序列&#xff08;Vec<u8>&#xff09;相互转换的常见方法是使用序列化和反序列化库。Rust 有一个流行的序列化库叫做 serde&#xff0c;它支持多种数据格式。为了将结构体转换为字节序列&#xff0c;…

保障餐饮场所安全:定期送检可燃气体报警器

在餐饮行业&#xff0c;火灾隐患一直备受关注。餐厅、茶饮店等场所常常使用燃气设备&#xff0c;而这些设备带来了潜在的安全隐患。 为了及时发现并预防可燃气体泄漏&#xff0c;可燃气体报警器的定期送检显得尤为重要。那么&#xff0c;为什么可燃气体报警器需要定期送检呢&a…

【MySQL精通之路】系统变量-动态系统变量

许多服务器系统变量是动态的&#xff0c;可以在运行时进行设置。 参见“变量赋值的SET语法”。 有关设置系统变量的权限要求的描述&#xff0c;请参阅“系统变量权限” 【MySQL精通之路】系统变量-系统变量权限-CSDN博客 下表列出了所有适用于mysqld的动态系统变量。 该表列出…

java 设计模式(三)-- 单例模式

引言 单例模式是一种常用的软件设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取这个实例。单例模式在Java中实现起来相对简单&#xff0c;但实现方式有多种&#xff0c;每种方式都有其特点和适用场景。 一、单例模式的实现方式 1. 懒汉…

【Linux】从零开始认识进程间通信 —— 管道

送给大家一句话&#xff1a; 人要成长&#xff0c;必有原因&#xff0c;背后的努力与积累一定数倍于普通人。所以&#xff0c;关键还在于自己。 – 杨绛 从零开始认识进程间通信 1 为什么要进程间通信2 进程如何通信3 进程通信的常见方式4 管道4.1 什么是管道4.2 管道通信的系…

交叉编译程序,提示 incomplete type “struct sigaction“ is not allowed

问题描述 incomplete type "struct sigaction" is not allowed解决办法 在代码的最顶端添加如下代码即可 #define _XOPEN_SOURCE此定义不是简单的宏定义&#xff0c;是使程序符合系统环境的不可缺少的部分 _XOPEN_SOURCE为了实现XPG&#xff1a;The X/Open Porta…

零一万物Yi-1.5开源,34B/9B/6B多尺寸,34B超Qwen1.5-72B

前言 近年来&#xff0c;大型语言模型&#xff08;LLM&#xff09;在各个领域展现出惊人的能力&#xff0c;为人们的生活和工作带来了巨大的改变。然而&#xff0c;大多数开源 LLM 的性能仍然无法与闭源模型相媲美&#xff0c;这限制了 LLM 在科研和商业领域的进一步应用。为了…

element-plus表格的表单校验如何实现,重点在model和prop

文章目录 vue&#xff1a;3.x element-plus&#xff1a;2.7.3 重点&#xff1a; 1) tableData放到form对象里 2) form-item的prop要写成tableData.序号.属性 <!--table-表单校验--> <template><el-form ref"forms" :model"form"><e…