Unity Image - 镜像

1、为什么要使用镜像

在游戏开发过程中,我们经常会为了节省 美术图片资源大小,美术会将两边相同的图片进行切一半来处理。如下所示一个按钮 需要 400 * 236,然而美术只需要切一张 74*236的大小就可以了。这样一来图集就可以容纳更多的图片。内存占用也更少。

2.实现方案

  1. 拷贝一张图片,然后把 scale改成-1,这种方法比较笨,需要多加一张图片,操作起来也很不方便。没啥好讲的。
  2. 拷贝原有的图片顶点,进行对称处理。如下图所示。

3.在哪里拿到mesh顶点?修改顶点?

BaseMeshEffect :是用于实现网格效果的抽象类实现IMeshModifier接口,是Shadow、Outline等效果的基类。
从Unity uGUI原理解析-Graphic可以知道,Graphic 在执行 DoMeshGeneration 时会获取到当前GameObject上的所有实现 IMeshModifier 的组件。 并且会调用 ModifyMesh 方法来修改当前Graphic 的Mesh数据。BaseMeshEffect 的类结构图如下:

我们编写一个MirrorImage来继承BaseMeshEffect,拿到 VertexHelper,获取网格的顶点流数据,然后进行下面的镜像,映射操作:

using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;

public enum MirrorType
{
    Horizontal,
    Vertical,
    HorizontalAndVertical
}

[RequireComponent(typeof(Image))]
public class MirrorImage : BaseMeshEffect
{
    public MirrorType mirrorType = MirrorType.Horizontal;

    public Dictionary<Image.Type, IMirror> MirrorDic = new Dictionary<Image.Type, IMirror>()
    {
        {Image.Type.Simple, new SimpleMirror()},
        {Image.Type.Sliced, new SlicedMirror()},
        {Image.Type.Tiled, new TiledMirror()},
        {Image.Type.Filled, new FilledMirror()},
    };

    public Image image => graphic as Image;

    public override void ModifyMesh(VertexHelper vh)
    {
        if (!IsActive()) return;
        Image.Type imageType = (graphic as Image).type;
        List<UIVertex> vertices = new List<UIVertex>();
        vh.GetUIVertexStream(vertices);
        vh.Clear();
        
        MirrorDic[imageType].Draw(image, vertices, mirrorType);
        vh.AddUIVertexTriangleStream(vertices);
    }

    [Button("Set Native Size")]
    public void SetNativeSize()
    {
        if (image.sprite != null)
        {
            float w = image.sprite.rect.width / image.pixelsPerUnit;
            float h = image.sprite.rect.height / image.pixelsPerUnit;
            image.rectTransform.anchorMax = image.rectTransform.anchorMin;
            float x = mirrorType == MirrorType.Horizontal || mirrorType == MirrorType.HorizontalAndVertical ? 2 : 1;
            float y = mirrorType == MirrorType.Vertical || mirrorType == MirrorType.HorizontalAndVertical ? 2 : 1;
            image.rectTransform.sizeDelta = new Vector2(w * x, h * y);
            image.SetAllDirty();
        }
    }
}

当然除了继承BaseMeshEffect,当然还可以直接继承Image,重写OnPopulateMesh。

4.如何实现顶点的镜像?

很简单 假设中心的是 center( 0,0),需要水平镜像的点 A(-1,0) 镜像后的点就是 B(-1,0)需要满足   A 到Center的距离 == B到center的距离:

所以我们先求出镜像的中心点 center的位置 ,因为矩形有自己的中心点位置,我们需要求出镜像的中心点位置在改矩形下的坐标,这么说可能有点绕看下图:

矩形的宽w:100,h:100,矩形自身的中心点(蓝色的圈) 这边为称为O点,在Unity中是以  Rect中的 x,y代表的是坐下角的点 既 (-75,-50),

对于Rect(x,y) 不理解的话可以看下GPT的回答,可能比我讲的清楚:

那么镜像真正的中心点坐标

  • center.x = rect.x + rect.width;
  • center.y = rect.y + rect.height;

那么要镜像的点 A 镜像后为B   ,需要求出A到center的长度大小然后取反 + 矩形中心点与镜像中心点的偏移

B.x = -(A.x - center.x) + (rect.x+rect.width/2)

B.y = -(A.y - center.y) + (rect.x+rect.width/2)

逻辑分析完了,直接看代码吧:


using System.Collections.Generic;
using UnityEngine;

public static class MirrorUtlis
{
    public static void Mirror(Rect rect,List<UIVertex> uiVertices,MirrorType type)
    {
        int count = uiVertices.Count;
        switch (type)
        {
            case MirrorType.Horizontal:
                Mirror(rect, uiVertices, type, count);
                break;
            case MirrorType.Vertical:
                Mirror(rect, uiVertices, type, count);
                break;
            case MirrorType.HorizontalAndVertical:
                Mirror(rect, uiVertices, MirrorType.Horizontal, count);
                Mirror(rect, uiVertices, MirrorType.Vertical, 2 * count);
                break;
        }
        RemoveVertices(uiVertices);
    }

    private static void Mirror(Rect rect, List<UIVertex> uiVertices, MirrorType type, int count)
    {
        for (int i = 0; i < count; i++)
        {
            UIVertex vertex = uiVertices[i];
            switch (type)
            {
                case MirrorType.Horizontal:
                    vertex = HorizontalMirror(rect, vertex);
                    break;
                case MirrorType.Vertical:
                    vertex = VerticalMirror(rect, vertex);
                    break;
                case MirrorType.HorizontalAndVertical:
                    vertex = HorizontalMirror(rect, vertex);
                    vertex = VerticalMirror(rect, vertex);
                    break;
            }
            uiVertices.Add(vertex);
        }
    }

    private static UIVertex HorizontalMirror(Rect rect, UIVertex vertex)
    {
        float center = rect.width / 2 + rect.x;
        vertex.position.x = -(vertex.position.x - center) + rect.x + rect.width/2;
        return vertex;
    }
    
    private static UIVertex VerticalMirror(Rect rect, UIVertex vertex)
    {
        float center = rect.height / 2 + rect.y;
        vertex.position.y = -(vertex.position.y - center) + rect.y + rect.height/2;
        return vertex;
    }
    
    // 移除构不成三角形的顶点
    private static void RemoveVertices(List<UIVertex> uiVertices)
    {
        int end = uiVertices.Count;

        for (int i = 2; i < end; i += 3)
        {
            UIVertex v1 = uiVertices[i];
            UIVertex v2 = uiVertices[i - 1];
            UIVertex v3 = uiVertices[i - 2];

            if (v1.position == v2.position ||
                v1.position == v3.position ||
                v2.position == v3.position)
            {
                // 移动到尾部
                ChaneVertices(uiVertices, i - 1, end - 3);
                ChaneVertices(uiVertices, i - 2, end - 2);
                ChaneVertices(uiVertices, i, end - 1);
                end -= 3;
            }
        }
        
        if(end < uiVertices.Count)
            uiVertices.RemoveRange(end,uiVertices.Count - end);
    }
    
    private static void ChaneVertices(List<UIVertex> uiVertices,int a,int b)
    {
        (uiVertices[a], uiVertices[b]) = (uiVertices[b], uiVertices[a]);
    }
}

在顶点镜像前我们需要对顶点进行顶点映射,什么时时映射?

如上图所示,原来图片是白色区域大小,顶点为白色图片的四个顶点,因为要做对称,所以需要留出一半的位置来增加映射后的顶点。

在不同模式下的顶点映射需要做不同处理,在Unity中有一下几种模式:

  • Simple: 如上面的图所示,对顶点的位置 除以 2 即可比较简单
  • Sliced:九宫格模式下因为要保留九宫格的顶点位置,不能让九宫格保留的位置发生形变,就不能直接除以2来处理,需要做平移处理。具体实现下面在讲
  • Filed:平铺模式下是不需要在镜像网格顶点的,因为平铺下的图片顶点是已经增加好的了,我们只需要对UV做镜像就可以了
  • Filled:暂时未实现该模式下的,以后有时间在研究。

由于有多种模式的处理我们将实现接口化,方便我们的管理:定义一个IMirror接口:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public interface IMirror
{
    void Draw(Image image,List<UIVertex> uiVertices,MirrorType type);
}

Simpe下的顶点映射:

public class SimpleMirror : IMirror
{
    public void Draw(Image image,List<UIVertex> uiVertices,MirrorType type)
    {
        ChangeVertices(image.rectTransform.rect,uiVertices,type);
        MirrorUtlis.Mirror(image.rectTransform.rect,uiVertices,type);
    }

    // 改变原有的顶点位置 (做一半映射) 如果是 Horizontal:左半部分 Vertical:上半部分 HorizontalAndVertical:左上四分之一
    private void ChangeVertices(Rect rect,List<UIVertex> uiVertices,MirrorType type)
    {
        for (int i = 0; i < uiVertices.Count; i++)
        {
            UIVertex vertex = uiVertices[i];
            switch (type)
            {
                case MirrorType.Horizontal:
                    vertex = HorizontalVertex(rect, vertex);
                    break;
                case MirrorType.Vertical:
                    vertex = VerticalVertex(rect, vertex);
                    break;
                case MirrorType.HorizontalAndVertical:
                    vertex = HorizontalVertex(rect, vertex);
                    vertex = VerticalVertex(rect, vertex);
                    break;
            }
            uiVertices[i] = vertex;
        }
    }

    // 水平映射
    private UIVertex HorizontalVertex(Rect rect,UIVertex vertex)
    {
        vertex.position.x = (vertex.position.x + rect.x) / 2;// - rect.width / 2;
        return vertex;
    }
    
    // 垂直映射
    private UIVertex VerticalVertex(Rect rect,UIVertex vertex)
    {
        vertex.position.y = (rect.y + vertex.position.y) / 2 + rect.height / 2;
        return vertex;
    }
}

Sliced下的顶点映射:

我们可以看到如下映射的主要方法:

// 水平映射
private UIVertex HorizontalVertex(Rect rect,UIVertex vertex)
{
    if (vertex.position.x == s_VertScratch[0].x || vertex.position.x == s_VertScratch[1].x) return vertex;
    vertex.position.x -= rect.width / 2;
    return vertex;
}

 时机上非常简单,就是直接对 x 做矩形宽度/2 的平移,比较难的是我们需要知道什么顶点需要做平移?

这个需要看一下Image的源码是如何实现顶点的处理:

        /// <summary>
        /// Generate vertices for a 9-sliced Image.
        /// </summary>
        private void GenerateSlicedSprite(VertexHelper toFill)
        {
            if (!hasBorder)
            {
                GenerateSimpleSprite(toFill, false);
                return;
            }

            Vector4 outer, inner, padding, border;

            if (activeSprite != null)
            {
                outer = Sprites.DataUtility.GetOuterUV(activeSprite);
                inner = Sprites.DataUtility.GetInnerUV(activeSprite);
                padding = Sprites.DataUtility.GetPadding(activeSprite);
                border = activeSprite.border;
            }
            else
            {
                outer = Vector4.zero;
                inner = Vector4.zero;
                padding = Vector4.zero;
                border = Vector4.zero;
            }

            Rect rect = GetPixelAdjustedRect();

            Vector4 adjustedBorders = GetAdjustedBorders(border / multipliedPixelsPerUnit, rect);
            padding = padding / multipliedPixelsPerUnit;

            s_VertScratch[0] = new Vector2(padding.x, padding.y);
            s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);

            s_VertScratch[1].x = adjustedBorders.x;
            s_VertScratch[1].y = adjustedBorders.y;

            s_VertScratch[2].x = rect.width - adjustedBorders.z;
            s_VertScratch[2].y = rect.height - adjustedBorders.w;

            for (int i = 0; i < 4; ++i)
            {
                s_VertScratch[i].x += rect.x;
                s_VertScratch[i].y += rect.y;
            }

            ......
            ......
        }

这段源码不全我截取了,计算图片的位置信息,然后把4个顶点位置信息按顺序写进s_VertScratch数组中。这四个位置分别对应一下位置:

即九宫格裁剪后映射到Image顶点上的四个位置,所以当我们向做水平映射的时候只需要平移和 3和4 x轴相等的顶点,与1和2 x轴相等的顶点保留原来的位置。如下图可以很直观的看出来

至于怎么算出这四个九宫格映射的顶点就直接拷贝Image源码的实现就好了。

贴一下完整代码:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SlicedMirror : IMirror
{
    private Image image;
    
    // 九宫格的四个分界点
    private Vector2[] s_VertScratch = new Vector2[4];

    public void Draw(Image image,List<UIVertex> uiVertices,MirrorType type)
    {
        this.image = image;
        SetVertScratch();
        ChangeVertices(image.rectTransform.rect,uiVertices,type);
        MirrorUtlis.Mirror(image.rectTransform.rect,uiVertices,type);
    }

    private void ChangeVertices(Rect rect,List<UIVertex> uiVertices,MirrorType type)
    {
        for (int i = 0; i < uiVertices.Count; i++)
        {
            UIVertex vertex = uiVertices[i];
            switch (type)
            {
                case MirrorType.Horizontal:
                    vertex = HorizontalVertex(rect, vertex);
                    break;
                case MirrorType.Vertical:
                    vertex = VerticalVertex(rect, vertex);
                    break;
                case MirrorType.HorizontalAndVertical:
                    vertex = HorizontalVertex(rect, vertex);
                    vertex = VerticalVertex(rect, vertex);
                    break;
            }
            uiVertices[i] = vertex;
        }
    }

    // 水平映射
    private UIVertex HorizontalVertex(Rect rect,UIVertex vertex)
    {
        if (vertex.position.x == s_VertScratch[0].x || vertex.position.x == s_VertScratch[1].x) return vertex;
        vertex.position.x -= rect.width / 2;
        return vertex;
    }
    
    // 垂直映射
    private UIVertex VerticalVertex(Rect rect,UIVertex vertex)
    {
        if (vertex.position.y == s_VertScratch[2].y || vertex.position.y == s_VertScratch[3].y) return vertex;
        vertex.position.y += rect.height / 2;
        return vertex;
    }
    
    private void SetVertScratch()
    {
        Sprite activeSprite = image.sprite;
        Vector4 padding, border;

        if (activeSprite != null)
        {
            padding = UnityEngine.Sprites.DataUtility.GetPadding(activeSprite);
            border = activeSprite.border;
        }
        else
        {
            padding = Vector4.zero;
            border = Vector4.zero;
        }

        Rect rect = image.GetPixelAdjustedRect();

        var multipliedPixelsPerUnit = image.pixelsPerUnit * image.pixelsPerUnitMultiplier;
        Vector4 adjustedBorders = GetAdjustedBorders(border / multipliedPixelsPerUnit, rect);
        padding /= multipliedPixelsPerUnit;

        s_VertScratch[0] = new Vector2(padding.x, padding.y);
        s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);

        s_VertScratch[1].x = adjustedBorders.x;
        s_VertScratch[1].y = adjustedBorders.y;

        s_VertScratch[2].x = rect.width - adjustedBorders.z;
        s_VertScratch[2].y = rect.height - adjustedBorders.w;

        for (int i = 0; i < 4; ++i)
        {
            s_VertScratch[i].x += rect.x;
            s_VertScratch[i].y += rect.y;
        }
    }
    private Vector4 GetAdjustedBorders(Vector4 border, Rect adjustedRect)
    {
        Rect originalRect = image.rectTransform.rect;

        for (int axis = 0; axis <= 1; axis++)
        {
            float borderScaleRatio;
            if (originalRect.size[axis] != 0)
            {
                borderScaleRatio = adjustedRect.size[axis] / originalRect.size[axis];
                border[axis] *= borderScaleRatio;
                border[axis + 2] *= borderScaleRatio;
            }

            float combinedBorders = border[axis] + border[axis + 2];
            if (adjustedRect.size[axis] < combinedBorders && combinedBorders != 0)
            {
                borderScaleRatio = adjustedRect.size[axis] / combinedBorders;
                border[axis] *= borderScaleRatio;
                border[axis + 2] *= borderScaleRatio;
            }
        }
        return border;
    }
}

Tiled:模式下的映射

在平铺模式下顶点都是完整的,不需要做镜像处理,只需要修改没一块对应的UV对称即可,我们固定开始位置为1,不做翻转,那么

2位置所构成的顶点UV.y:就需要做对称处理

4位置所构成的顶点UV.x:就需要做对称处理

3位置所构成的顶点UV.x和y :都需要做对称处理

如何判断某几个顶点的UV需要做对称?
我们知道一个三角面由三个顶点构成,那么我么只需要找出三个顶点的中心位置,假设sprite的宽高都为w = 100,h = 100,构成的三角形区域的中心点分别为

1位置 =>(50,50),

2位置 => (50,150),

3位置 => (150,150),

4位置 => (150,50),

我们对中心的的

1 => x / W = 0.5

2 => x / W = 1.5

。。

我们对结果 %2 那么 结果为 1 的就需要翻转了;

uv怎么翻转
outerUv = UnityEngine.Sprites.DataUtility.GetOuterUV(image.sprite);

outerUV中的  x,y就代表右下角的点,z就代表宽,w:代表高,假设A镜像UV后的点为B,那么就满足:

A 到 (0,0)的距离  == B到(0+z)的距离,所以

镜像后的 A.x = outer.z -( A.x - outerUv.x )

贴一下完整代码

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class TiledMirror : IMirror
{

    private Vector4 outerUv;
    public void Draw(Image image,List<UIVertex> uiVertices,MirrorType type)
    {
        outerUv = UnityEngine.Sprites.DataUtility.GetOuterUV(image.sprite);
        ChangeUv(uiVertices,type);
    }

    // uv翻转
    private void ChangeUv(List<UIVertex> uiVertices,MirrorType type)
    {
        Vector3 cellMinP = uiVertices[0].position;
        Vector3 cellMaxP = uiVertices[2].position;
        float w = cellMaxP.x - cellMinP.x;
        float h = cellMaxP.y - cellMinP.y;
        
        for (int i = 0; i < uiVertices.Count; i+= 3)
        {
            UIVertex v1 = uiVertices[i];
            UIVertex v2 = uiVertices[i+1];
            UIVertex v3 = uiVertices[i+2];
            
            float centerX = GetCenter(v1, v2, v3, true) - cellMaxP.x;
            float centerY = GetCenter(v1, v2, v3, false) - cellMaxP.y;
            
            bool changeX = Mathf.Ceil(centerX / w) % 2 != 0;
            bool changeY = Mathf.Ceil(centerY / h) % 2 != 0;
            
            if (changeX && (type == MirrorType.Horizontal || type == MirrorType.HorizontalAndVertical))
            {
  
                v1 = HorizontalUv(v1);
                v2 = HorizontalUv(v2);
                v3 = HorizontalUv(v3);
            }
            
            if (changeY && (type == MirrorType.Vertical || type == MirrorType.HorizontalAndVertical))
            {
                v1 = VerticalUv(v1);
                v2 = VerticalUv(v2);
                v3 = VerticalUv(v3);
            }
            
            uiVertices[i] = v1;
            uiVertices[i + 1] = v2;
            uiVertices[i + 2] = v3;
        }
    }

    // 获取三个顶点的中心位置
    private float GetCenter(UIVertex v1,UIVertex v2,UIVertex v3,bool isX)
    {
        float min = Mathf.Min(
            Mathf.Min(isX ? v1.position.x : v1.position.y,isX ? v2.position.x : v2.position.y),
            Mathf.Min(isX ? v1.position.x : v1.position.y,isX ? v3.position.x : v3.position.y));
        float max = Mathf.Max(
            Mathf.Max(isX ? v1.position.x : v1.position.y,isX ? v2.position.x : v2.position.y),
            Mathf.Max(isX ? v1.position.x : v1.position.y,isX ? v3.position.x : v3.position.y));
        return (min + max) / 2;
    }

    private UIVertex HorizontalUv(UIVertex vertex)
    {
        vertex.uv0.x = outerUv.x + outerUv.z - vertex.uv0.x;
        return vertex;
    }
    
    private UIVertex VerticalUv(UIVertex vertex)
    {
        vertex.uv0.y = outerUv.y + outerUv.w - vertex.uv0.y;
        return vertex;
    }
    
    
}

总结:

实现过程中,思路理清之后实现基本上是不难,但是需要去理解Unity Image的实现,针对绘制不同模式的生成网格顶点的实现,知道图篇是怎么绘制上去的,三个顶点构成一个面,Rect中的(x,y,z,w)分别代表什么。然后就是计算计算。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis数据存储:高效、灵活、实时

目录 引言 1. Redis概述 1.1 什么是Redis&#xff1f; 1.2 Redis的数据结构 1.3 Redis的持久化机制 2. Redis的使用场景 2.1 缓存 2.2 会话存储 2.3 发布/订阅系统 2.4 计数器和排行榜 3. Redis最佳实践 3.1 数据模型设计 3.2 键的命名规范 3.3 事务和原子操作 3…

人工智能学习5(特征抽取)

编译环境&#xff1a;PyCharm 文章目录 编译环境&#xff1a;PyCharm 特征抽取无监督特征抽取(之PCA)代码实现鸢尾花数据集无监督特征抽取 有监督特征抽取(之LDA)代码实现,生成自己的数据集并进行有监督特征抽取(LDA)生成自己的数据集PCA降维和LDA降维对比 代码实现LDA降维对鸢…

UiPath学习笔记

文章目录 前言RPA介绍UiPath下载安装组件内容 前言 最近有一个项目的采集调研涉及到了客户端的采集&#xff0c;就取了解了一下RPA和UIPATH&#xff0c;记录一下 RPA介绍 RPA&#xff08;Robotic Process Automation&#xff1a;机器人处理自动化&#xff09;&#xff0c;是…

工业机器视觉megauging(向光有光)使用说明书(十三,资源发现bug,已经更新)

megauging&#xff08;向光有光&#xff09;旧资源有bug&#xff0c;已经更新&#xff0c;如下&#xff1a; 第一工具&#xff0c;combox默认0&#xff0c;选择后&#xff0c;鼠标点击“获取结果”&#xff0c;相机就取一帧图像处理后显示出来&#xff1a; 第一工具&#xff0…

使用dirmap命令行时报错,提示缺少gevent模块

记得以前是可以的&#xff0c;可能是时间长了重装了系统&#xff0c;引起的。 修复方法。升级pip&#xff0c;然后重新下载安装gevent模块。 具体&#xff1a; python -m pip install --upgrade pip 使用下面命令解决下载慢的问题。 pip config set global.index-url http…

已知数组A[1..n]中元素类型为非负整数,设计算法将其调整为左右两部分,左边所有为奇数,右边所有为偶数,并要求算法的时间复杂度为O(n)

//左边奇数右边偶数 void Swap(int* a, int* b) {int tmp *b;*b *a;*a tmp; } void LeftRight(int arr[],int n) {int i 0;int j n - 1;while(i<j){if (arr[i] % 2 0 && arr[j] % 2 1) {Swap(&arr[i], &arr[j]);i;j--;}else if (arr[i] % 2 1 &…

SmartSoftHelp8,数据库字段详细文档自动生成工具

数据库开发文档自动生成 包括数据库设计详细信息&#xff1a; 数据库字段名称&#xff0c;数据类型&#xff0c;大小&#xff0c;是否主键&#xff0c;说明等 一键自动生成开发需求文档 导出html 格式方便查询 下载地址 https://pan.baidu.com/s/1zBgeYsqWnSlNgiKPR2lUYg…

号称要做人民货币的Spacemesh,有何新兴叙事?

​打开Spacemesh的官网&#xff0c;率先映入眼帘的是一个响亮的口号——On a quest to become the people’s coin&#xff08;致力于成为人民的货币&#xff09;&#xff01;Spacemesh 联合创始人 Tomer Afek 曾表示“Spacemesh 的低准入门槛和激励兼容性&#xff0c;激发了从…

css所有属性介绍

文章目录 1️⃣ CSS属性介绍1.1 CSS3 动画属性&#xff08;Animation&#xff09;1.2 CSS 背景属性&#xff08;Background&#xff09;1.3 CSS 边框属性&#xff08;Border 和 Outline&#xff09;1.4 Box 属性1.5 Color 属性1.6 Content for Paged Media 属性1.7 CSS 尺寸属性…

C语言——深入理解指针(4)

目录 1.回调函数 2. qsort 函数的使用 2.1 排序整型数据 2.2 排序结构体数据 3. qsort 函数的模拟实现 1.回调函数 回调函数就是通过一个函数指针调用的函数。 你把函数的地址作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数时&#xff0c;被调…

服务器基础知识

服务器被誉为互联网之魂。我第一次见到服务器是在学校图书馆&#xff0c;是一种机架式服务器&#xff0c;第二次见到服务器是在公司机房。本期文章是对服务器进行大盘点和梳理&#xff0c;会介绍我拆装服务器过程&#xff0c;从中的学习感悟。 图片来自 Pexels 01 服务器 服务…

7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动

在笔者上一篇文章《内核监视LoadImage映像回调》中LyShark简单介绍了如何通过PsSetLoadImageNotifyRoutine函数注册回调来监视驱动模块的加载&#xff0c;注意我这里用的是监视而不是监控之所以是监视而不是监控那是因为PsSetLoadImageNotifyRoutine无法实现参数控制&#xff0…

[IIS服务]搭建unityWebGl项目服务器(用idea失败了,这次用IIS)

1、确认安装服务 没有安装的&#xff0c;点击安装&#xff0c;安装完成后下一步。 2、配置IIS服务&#xff08;很多小伙伴更新了windows找不到&#xff0c;可以使用cmd运行control admintools打开下图页面&#xff09; 打开管理器之后添加一个网站。 路径选择网站路径&#xf…

【JVM】一篇通关JVM类加载与字节码技术

目录 1. 类文件结构1-1. 魔数 版本 常量池 2. 字节码指令3. 编译期处理4. 类加载阶段5. 类加载器6. 运行期优化 类加载与字节码技术 1. 类文件结构 案例 // HelloWorld 示例 public class HelloWorld {public static void main(String[] args) {System.out.println("h…

策略模式与简单工厂模式:终结if-else混乱,让代码更清爽

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概4500多字&#xff0c;预计阅读时间长需要5分钟。本篇文章的实战性、理论性较强&#xff0c;是一篇质量分数较高的技术干货文章&#x…

探索 Web API:SpeechSynthesis 与文本语言转换技术

一、引言 随着科技的不断发展&#xff0c;人机交互的方式也在不断演变。语音识别和合成技术在人工智能领域中具有重要地位&#xff0c;它们为残障人士和日常生活中的各种场景提供了便利。Web API 是 Web 应用程序接口的一种&#xff0c;允许开发者构建与浏览器和操作系统集成的…

记录 | photoshop移动选区

期望达到的效果&#xff1a; 选择一块区域&#xff0c;并移动它 操作&#xff1a; (1) 选择矩形选框工具&#xff0c; (2) 对区域进行选取&#xff0c; (3) 选择移动工具&#xff0c; (4) 移动选区&#xff0c;效果如下&#xff0c;

哪些AI软件有消除笔?这四款AI软件轻松消除水印

日常生活或工作中&#xff0c;离不开对图片的处理&#xff0c;AI已经对图片视频下手了&#xff0c;处理软件我们不必在用传统的PS来一点点扣了&#xff0c;AI能瞬间消除图片中多余的杂物&#xff0c;大大提高了打工人的工作效率&#xff0c;那么哪些AI软件有消除笔的功能呢&…

System.out.println隐藏字符串

昨天开发的时候遇到一个坑&#xff0c;这个坑几乎浪费了我一整天时间&#xff0c;我甚至现在都不知道其原因。 开发环境 macOS Ventura 13.4 IntelliJ IDEA 2023.1.2 现象 我用java的各种httpclient获取网络上的一个文本文件&#xff0c;获取的文本文件的内容使用System.ou…

软著项目推荐 深度学习手势识别算法实现 - opencv python

文章目录 1 前言2 项目背景3 任务描述4 环境搭配5 项目实现5.1 准备数据5.2 构建网络5.3 开始训练5.4 模型评估 6 识别效果7 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习手势识别算法实现 - opencv python 该项目较为新颖…