【我的渲染技术进阶之旅】glfw库简单介绍

news2024/11/13 14:47:40

文章目录

  • 一、为啥去了解glfw?
  • 二、glfw相关资料
  • 三、glfw简单示例
    • 1. 引入 GLFW 头文件
    • 2. 初始化和终止 GLFW
    • 3. 设置错误回调
    • 4. 创建窗口和上下文
    • 5.使 OpenGL 上下文成为当前上下文
    • 6. 检查窗口关闭标志
    • 7.接收输入事件
    • 8.使用 OpenGL 渲染
    • 9. 读取定时器
    • 10.交换缓冲区
    • 11. 处理事件
    • 12. 综合应用一下

一、为啥去了解glfw?

在学习OpenGL的时候,很多项目都使用了glfw库。

  • 游戏引擎 浅入浅出 : 2.2 搭建Opengl开发环境
  • Learn Opengl:创建窗口
  • Learn Opengl:你好,窗口

在这里插入图片描述
下面一段代码就使用GLFW创建了一个窗口,并设置了清屏颜色,如下所示:
在这里插入图片描述

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }    

    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

上面代码很快就创建了一个窗口,为了看得懂代码,我们就得去学一下什么是glfw。

二、glfw相关资料

  • glfw的github地址
  • glfw文档
  • glfw Getting started
  • glfw下载地址

GLFW 是一个开源的多平台库,用于桌面上的 OpenGLOpenGL ESVulkan 开发。它提供了一个简单的 API,用于创建窗口、上下文和表面,接收输入和事件。

GLFWC 语言编写,支持 Windows、macOS、X11 和 Wayland

GLFW 在 zlib/libpng 许可证下获得许可。

在这里插入图片描述

三、glfw简单示例

1. 引入 GLFW 头文件

在使用 GLFW 的应用程序的源文件中,您需要包含它的头文件。

#include <GLFW/glfw3.h>

他的头文件提供了 GLFW API 的所有常量、类型和函数原型。

默认情况下,它还包括来自您的开发环境的 OpenGL 标头。在某些平台上,此标头仅支持旧版本的 OpenGL。最极端的情况是 Windows,它通常只支持 OpenGL 1.2

大多数程序将改为使用扩展加载程序库并包含其标头。此示例使用由 glad 生成的文件。 GLFW 标头可以检测到大多数此类标头,如果它们首先包含在内,然后将不包含来自您的开发环境的标头。

#include <glad/gl.h>
#include <GLFW/glfw3.h>

为确保不存在标头冲突,您可以在 GLFW 标头之前定义 GLFW_INCLUDE_NONE 以显式禁用包含开发环境标头。这也允许以任何顺序包含两个标头。

#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glad/gl.h>

2. 初始化和终止 GLFW

在您可以使用大多数 GLFW 函数之前,必须先初始化该库。初始化成功后,返回 GLFW_TRUE。如果发生错误,则返回 GLFW_FALSE

if (!glfwInit())
{
    // Initialization failed
}

请注意,GLFW_TRUEGLFW_FALSE 始终是 10。 当您使用完 GLFW 后,通常就在应用程序退出之前,您需要终止 GLFW

glfwTerminate();

这会销毁任何剩余的窗口并释放 GLFW 分配的任何其他资源。在这个调用之后,您必须在使用任何需要它的 GLFW 函数之前再次初始化 GLFW

3. 设置错误回调

大多数事件都是通过回调报告的,无论是按键被按下、GLFW 窗口被移动还是发生错误。回调是 GLFW 使用描述事件的参数调用的 C 函数(或 C++ 静态方法)。

如果 GLFW 函数失败,则会向 GLFW 错误回调报告错误。您可以通过错误回调接收这些报告。此函数必须具有以下签名,但可以执行其他回调中允许的任何操作。

void error_callback(int error, const char* description)
{
    fprintf(stderr, "Error: %s\n", description);
}

必须设置回调函数,以便 GLFW 知道调用它们。设置错误回调的函数是为数不多的可以在初始化之前调用的 GLFW 函数之一,它可以让您在初始化期间和之后收到错误通知。

glfwSetErrorCallback(error_callback);

4. 创建窗口和上下文

窗口及其 OpenGL 上下文是通过一次调用 glfwCreateWindow 来创建的,它会返回创建的组合窗口和上下文对象的句柄

GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
if (!window)
{
    // Window or OpenGL context creation failed
}

这将创建一个带有 OpenGL 上下文的 640 x 480 窗口模式窗口。如果窗口或 OpenGL 上下文创建失败,将返回 NULL。您应该始终检查返回值。虽然窗口创建很少会失败,但上下文创建取决于正确安装的驱动程序,甚至在具有必要硬件的机器上也可能会失败。

默认情况下,OpenGL上下文GLFW创建可能具有任何版本。您可以通过设置GLFW_CONTEXT_VERSION_MAJORGLFW_CONTEXT_VERSION_MINOR提示创建之前,您可以使用最小的OpenGL版本。如果在机器上不支持所需的最小版本,则上下文(和窗口)的创建失败。

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
if (!window)
{
    // Window or context creation failed
}

窗口句柄被传递给所有与窗口相关的函数,并提供给所有与窗口相关的回调函数,因此它们可以判断哪个窗口接收到事件。

当不再需要窗口和上下文时,将其销毁。

glfwDestroyWindow(window);

一旦调用此函数,将不再为该窗口传递任何事件,并且其句柄将变为无效。

5.使 OpenGL 上下文成为当前上下文

在使用 OpenGL API 之前,您必须拥有当前的 OpenGL 上下文。

glfwMakeContextCurrent(window);

上下文将保持当前,直到您使另一个上下文成为当前上下文或直到拥有当前上下文的窗口被销毁。

如果您使用扩展加载器库来访问现代 OpenGL,那么这是初始化它的时候,因为加载器需要从当前上下文加载。此示例使用 glad,但同样的规则适用于所有此类库。

gladLoadGL(glfwGetProcAddress);

6. 检查窗口关闭标志

每个窗口都有一个标志,指示该窗口是否应该关闭。

当用户尝试关闭窗口时,通过按标题栏中的关闭小部件或使用 Alt+F4 等组合键,此标志设置为 1。请注意,窗口实际上并未关闭,因此您应该监视此标志并销毁窗口或向用户提供某种反馈。

while (!glfwWindowShouldClose(window))
{
    // Keep running
}

当用户试图通过使用 glfwSetWindowCloseCallback 设置关闭回调来关闭窗口时,您会收到通知。设置关闭标志后将立即调用回调。

您也可以使用 glfwSetWindowShouldClose 自行设置。如果您想将其他类型的输入解释为关闭窗口,这可能很有用,例如按下 Escape 键。

7.接收输入事件

每个窗口都有大量的回调,可以设置为接收各种事件。要接收按键按下和释放事件,请创建按键回调函数。

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

与其他窗口相关的回调一样,键回调是为每个窗口设置的。

glfwSetKeyCallback(window, key_callback);

为了在事件发生时调用事件回调,您需要按如下所述处理事件。

8.使用 OpenGL 渲染

一旦有了当前的 OpenGL 上下文,就可以正常使用 OpenGL。在本教程中,将渲染一个多色旋转三角形。需要为 glViewport 检索帧缓冲区大小。

int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);

您还可以使用 glfwSetFramebufferSizeCallback 设置帧缓冲区大小回调,并在大小更改时收到通知。

如何使用 OpenGL 进行渲染的详细信息不在本教程的讨论范围内,但是有许多学习现代 OpenGL 的优秀资源。这里有几个:

  • Anton’s OpenGL 4 Tutorials
  • Learn OpenGL
  • Open.GL

这些都碰巧使用了 GLFW,但是无论您使用什么 API 来创建窗口和上下文,OpenGL 本身的工作方式都是一样的。

9. 读取定时器

要创建流畅的动画,需要一个时间源。 GLFW 提供了一个计时器,它返回自初始化以来的秒数。使用的时间源在每个平台上都是最准确的,通常具有微秒级或纳秒级分辨率。

double time = glfwGetTime();

10.交换缓冲区

GLFW 窗口默认使用双缓冲。这意味着每个窗口都有两个渲染缓冲区;一个前台缓冲区和一个后台缓冲区。前台缓冲区是正在显示的缓冲区,后台缓冲区是您渲染到的缓冲区。

渲染完整个帧后,缓冲区需要相互交换,因此后台缓冲区变为前台缓冲区,反之亦然

glfwSwapBuffers(window);

交换间隔指示在交换缓冲区之前要等待多少帧,通常称为 vsync。默认情况下,交换间隔为零,这意味着缓冲区交换将立即发生。在快速机器上,许多帧永远不会被看到,因为屏幕仍然通常每秒更新 60-75 次,因此这会浪费大量 CPU 和 GPU 周期。

此外,由于缓冲区将在屏幕更新中间交换,导致屏幕撕裂。

由于这些原因,应用程序通常希望将交换间隔设置为 1。它可以设置为更高的值,但通常不推荐这样做,因为它会导致输入延迟。

glfwSwapInterval(1);

此函数作用于当前上下文,除非上下文是当前的,否则将失败。

11. 处理事件

GLFW 需要定期与窗口系统通信,以便接收事件并显示应用程序尚未锁定。当您有可见窗口时,必须定期进行事件处理,并且通常在缓冲区交换后每帧进行一次。

有两种处理未决事件的方法;轮询和等待。这个例子将使用事件轮询,它只处理那些已经收到的事件,然后立即返回。

glfwPollEvents();

这是连续渲染时的最佳选择,就像大多数游戏一样。相反,如果您只需要在收到新输入后更新渲染,glfwWaitEvents 是更好的选择。它会等到至少接收到一个事件,同时让线程休眠,然后处理所有接收到的事件。这节省了大量的 CPU 周期,并且对许多类型的编辑工具都很有用.

12. 综合应用一下

既然您知道了如何初始化 GLFW、创建窗口并轮询键盘输入,就可以创建一个简单的程序了。

该程序创建一个 640 x 480 的窗口模式窗口,并启动一个循环来清除屏幕、呈现三角形并处理事件,直到用户按下 Escape 或关闭窗口。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



#include <glad/gl.h>

#define GLFW_INCLUDE_NONE

#include <GLFW/glfw3.h>

#include "linmath.h"

#include <stdlib.h>
#include <stdio.h>

static const struct {
    float x, y;
    float r, g, b;
} vertices[3] =
        {
                {-0.6f, -0.4f, 1.f, 0.f, 0.f},
                {0.6f,  -0.4f, 0.f, 1.f, 0.f},
                {0.f,   0.6f,  0.f, 0.f, 1.f}
        };

static const char *vertex_shader_text =
        "#version 110\n"
        "uniform mat4 MVP;\n"
        "attribute vec3 vCol;\n"
        "attribute vec2 vPos;\n"
        "varying vec3 color;\n"
        "void main()\n"
        "{\n"
        "    gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
        "    color = vCol;\n"
        "}\n";

static const char *fragment_shader_text =
        "#version 110\n"
        "varying vec3 color;\n"
        "void main()\n"
        "{\n"
        "    gl_FragColor = vec4(color, 1.0);\n"
        "}\n";

static void error_callback(int error, const char *description) {
    fprintf(stderr, "Error: %s\n", description);
}

static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

int main(void) {
    GLFWwindow *window;
    GLuint vertex_buffer, vertex_shader, fragment_shader, program;
    GLint mvp_location, vpos_location, vcol_location;

    glfwSetErrorCallback(error_callback);

    if (!glfwInit())
        exit(EXIT_FAILURE);

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);

    window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
    if (!window) {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwSetKeyCallback(window, key_callback);

    glfwMakeContextCurrent(window);
    gladLoadGL(glfwGetProcAddress);
    glfwSwapInterval(1);

    // NOTE: OpenGL error checks have been omitted for brevity

    glGenBuffers(1, &vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
    glCompileShader(vertex_shader);

    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
    glCompileShader(fragment_shader);

    program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);

    mvp_location = glGetUniformLocation(program, "MVP");
    vpos_location = glGetAttribLocation(program, "vPos");
    vcol_location = glGetAttribLocation(program, "vCol");

    glEnableVertexAttribArray(vpos_location);
    glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
                          sizeof(vertices[0]), (void *) 0);
    glEnableVertexAttribArray(vcol_location);
    glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
                          sizeof(vertices[0]), (void *) (sizeof(float) * 2));

    while (!glfwWindowShouldClose(window)) {
        float ratio;
        int width, height;
        mat4x4 m, p, mvp;

        glfwGetFramebufferSize(window, &width, &height);
        ratio = width / (float) height;

        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT);

        mat4x4_identity(m);
        mat4x4_rotate_Z(m, m, (float) glfwGetTime());
        mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
        mat4x4_mul(mvp, p, m);

        glUseProgram(program);
        glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat *) mvp);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwDestroyWindow(window);

    glfwTerminate();
    exit(EXIT_SUCCESS);
}

本教程仅使用了 GLFW 提供的众多功能中的一小部分。 GLFW 涵盖的每个领域都有指南。每个指南将介绍该类别的所有功能。

  • Introduction to the API
  • Window guide
  • Context guide
  • Monitor guide
  • Input guide

您可以通过单击访问任何 GLFW 功能的参考文档,每个功能的参考链接到相关功能和指南部分。

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

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

相关文章

【C++】了解设计模式,模拟实现栈和队列

文章目录一.设计模式二.stack的模拟实现三.queue的模拟实现四.了解deque五、题目练习一.设计模式 设计模式有很多种&#xff0c;根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software&#xff08;中文译名&#xff1a;设计模式 - 可复用的面…

SpringBoot+VUE前后端分离项目学习笔记 - 【11 SpringBoot代码生成器_MybatisPlus】

引入mp生成器依赖 pom.xml <!-- 代码生成器 --> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version> </dependency> <dependency><grou…

RabbitMQ 单机安装-CentOS

RabbitMQ 单机安装-CentOS 官网查看RabbitMQ和对应的Erlang版本 进入 RabbitMQ 官网 &#xff0c;点击 顶上的 Get Started 点击Download Installation 点击左侧的Erlang Versions 查看对应版本 根据自己需要安装的RabbitMQ版本&#xff0c;找到需要Erlang的版本。 下…

什么是Spring Cloud?Spring Cloud介绍

简介Spring Cloud项目的官方网址&#xff1a;https://projects.spring.io/spring-cloud/Spring Cloud 并不是一个项目&#xff0c;而是一组项目的集合。在 Spring Cloud中包含了很多的子项目&#xff0c;每一个子项目都是一种微服务开发过程中遇到的问题的一种解决方案。它利用…

红中群内每日分享题目解析——第一天

目录 题目一&#xff1a;最后一次用的英雄 题目二&#xff1a;狗哥去哪 摩斯密码 题目三&#xff1a;黑丝白丝还有什么丝 题目一&#xff1a;最后一次用的英雄 ​​​​​​ 感谢我徒弟(不是 告知并解出此题 杭椒的博客_CSDN博客-网安经验分享,网络安全工具,CISP-PTE备考笔记…

音视频开发常用工具

目录 1.VLC播放器简介 1.1 VLC 播放器 1.2 VLC的功能列表 1.3 VLC播放网络串流 1.4 VLC作为流媒体服务器 2. MediaInfo简介 2.1 MediaInfo 2.1.1 获取多媒体文件信息 2.1.2 支持的格式 2.1.3 .查看方式 2.1.4 国际化 2.2 MediaInfo使用方法 2.3 MediaInfo参数说明 3…

将powershell、cmd和vscode终端的编码永久修改成utf-8

powershell修改方法 1、以管理员身份打开powershe New-Item $PROFILE -ItemType File -Force 2、打开C盘&#xff0c;找到我的文档中的WindowsPowerShell文件夹 3、编辑这个ps1文件&#xff08;默认是空的&#xff09;&#xff0c;加上以下代码 $OutputEncoding [console…

Handler的消息机制与消息延迟代码实现

Handler的作用 Handler消息机制在Android中的应用非常广泛&#xff0c;很多组件的底层实现都是靠Handler来完成的&#xff0c;所以掌握Handler消息机制的原理还是非常重要的。Handler的主要功能有两点&#xff1a; 1.它可以在不同的线程之间传递消息 我们都知道Andorid中规定…

menuconfig与Kconfig入门学习

概述 menuconfig是Linux平台用于管理代码工程、模块及功能的实用工具。 menuconfig的使用方式通常是在编译系统之前在系统源代码根目录下执行make menuconfig命令从而打开一个图形化配置界面&#xff0c;再通过对各项的值按需配置从而达到影响系统编译结果的目的。 Nuttx的m…

新建一个SpringMVC项目

步骤1:创建Maven项目 打开IDEA,创建一个新的web项目 步骤2:补全目录结构 因为使用骨架创建的项目结构不完整&#xff0c;需要手动补全 步骤3:导入jar包 将pom.xml中多余的内容删除掉&#xff0c;再添加SpringMVC需要的依赖 <?xml version"1.0" encoding"UT…

一文读懂远程线程注入

在红队行动中&#xff0c;红队的目的都是要在不暴露自身行动的前提下&#xff0c;向蓝队发动攻击。他们使用各种技术和程序来隐藏C2连接和数据流。攻击活动的第一步是获得初始访问权。他们会使用定制的恶意软件和有效载荷来躲避防杀软和EDR等防御工具。 在这些典型的攻击活动中…

代码中大量爆红,IDE设置jdk版本,及设置后无效的解决

代码大量爆红&#xff0c;检查ide设置的jdk版本是否与项目的java版本一致&#xff0c;做以下配置 1.project structrue 2.maven 3.如果以上设置以后没有效果&#xff0c;就把项目中的.ide文件删除&#xff0c;并且pom里面加上下面的代码&#xff0c;重启IDE。 <build&g…

植物大战僵尸:植物栏无冷却的找法

通过使用CE遍历内存&#xff0c;定位到植物无冷却的核心代码处&#xff0c;并通过修改关键的跳转来实现所有植物的无冷却。 第一种遍历修改方式 无冷却的遍历技巧1&#xff1a; 打开CE->回到游戏种植一颗向日葵 -> 扫描未知初始数值然后切回游戏 ->马上切回CE ->…

< JavaScript技术分享: 大文件切片上传 及 断点续传思路 >

文章目录&#x1f449; 前言及含义切片上传断点续传&#x1f449; 一、实现思路&#x1f449; 二、使用场景&#x1f449; 参考文献&#x1f449; 伸手党福利&#xff1a; 即拿即用&#xff08;前/后端思路均有&#xff09;往期内容 &#x1f4a8;&#x1f449; 前言及含义 在…

MVVM学习

mvvm基础知识Model&#xff1a;repository Entity 数据库 网络访问等对数据进行直接操作的代码View:视图代码 xml activity fragment adapter 等ui层的一些代码ViewModel:视图模型 用来和View ,Model层交互&#xff0c;将Model层的数据显示到View上&#xff0c;并处理View层的事…

Android Softap Mac地址随机化

目录 Android Q以后 Android Q 获取方式 Android Q以后 在SoftapManager创建的时候,可以跟下 Android Q Q:WifiManager#getWifiApConfiguration()只能获取到SSID和pwd等,无法获取到对应的BSSID。 原因分析:Android Q Softap的MAC地址设置是在HAL层,并没有给Framework赋…

result.isAck()报错

IDEA 的 bug&#xff0c;两张图解释

图像平滑处理

平滑处理是指在某些数学和统计分析中&#xff0c;对于原始数据做出拟合&#xff0c;并生成更平滑的数据。这通常是为了抑制原始数据中的离群点或噪声&#xff0c;使得数据更具可读性和可解释性。 在图像处理中常用于模糊处理和降低噪声。平滑滤波器使用给定邻域内的像素平均灰度…

Word处理控件Aspose.Words功能演示:在 C# .NET 中将 Word 转换为 PDF - 完整指南

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

JS面试题--JavaScript基础

1. new操作符的实现原理 创建一个对象将构造函数的作用域赋给新对象&#xff08;也就是将对象的__proto__属性指向构造函数的prototype属性&#xff09;指向构造函数中的代码&#xff0c;构造函数中的this指向该对象&#xff08;也就是为这个对象添加属性和方法&#xff09;返…