Linux基础项目开发1:量产工具——文字系统(四)

news2024/11/19 11:22:22

前言:

前面我们已经把显示系统,输入系统的框架搭建好了,那么有了输入和显示,显示的内容应该是什么呢?这节就要让我们一起对显示的内容,文字系统进行搭建。

目录

一、数据结构抽象

1.描述一个文字的位图:

2.描述一个字库操作:

3.font_manager.h

二、实现Freetype代码

freetype.c

 三、文字管理

font_manager.c

四、单元测试 

1.font_test.c 

2.disp_manager.c

3.disp_manager.h 

4. common.h

5.unittest下的Makefile

6.font下的Makefile

7.顶层的Makefile 

五、上板测试


 

一、数据结构抽象

 描述字符的方式:1.位置、大小   2.点阵

        点阵可以从固定大小的点阵字体文件中获得,也可以从Freetype的矢量字体文件中获得,所以我们需要抽象出一个结构体用来描述这些字符,一个结构体描述一个文字的位图,一个结构体描述一个字库操作。

1.描述一个文字的位图:

2.描述一个字库操作:

        使用点阵绘制文字时:每个文字的大小一样,前后文件互不影响 ,使用Freetype绘制文字时:大小可能不同,前面文字会影响后面文字

 

对于单个Freetype字符,格式如下 

         我们要抽象出一个结构体FontBitMap,能描述一个“字符”:位置、大小、位图,我们还要抽象出一个结构体FontOpr,能描述字体的操作,比如Freetype的操作、固定点阵字体的操作

3.font_manager.h

#ifndef _FONT_MANAGER_H
#define _FONT_MANAGER_H


#include <common.h>


typedef struct FontBitMap {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iRows;
	int iCurOriginX;
	int iCurOriginY;
	int iNextOriginX;
	int iNextOriginY;
	unsigned char *pucBuffer;
}FontBitMap, *PFontBitMap;

typedef struct FontOpr {
	char *name;
	int (*FontInit)(char *aFineName);
	int (*SetFontSize)(int iFontSize);
	int (*GetFontBitMap)(unsigned int dwCode, PFontBitMap ptFontBitMap);
	struct FontOpr *ptNext;
}FontOpr, *PFontOpr;

void RegisterFont(PFontOpr ptFontOpr);

void FontsRegister(void);

int SelectAndInitFont(char *aFontOprName, char *aFontFileName);
int SetFontSize(int iFontSize);
int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap);


#endif




第28行:链表

第30行:注册单个字符库 ,从链表里挑出想要的字库

第32行:在g_ptFonts链表里面挑出来你想要的字体文件

第33行:将找到的字体设置字体大小

第34行:将找到的字体设置位图

为了方便后续使用区域,我们进行如下操作,将区域定义到一个结构体上Region tRegion;

typedef struct FontBitMap {
	Region tRegion;
	int iCurOriginX;
	int iCurOriginY;
	int iNextOriginX;
	int iNextOriginY;
	unsigned char *pucBuffer;
}FontBitMap, *PFontBitMap;

这个 tRegiondisp manager.h中有声明

typedef struct Region {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iHeigh;
}Region, *PRegion;

二、实现Freetype代码

freetype.c

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <font_manager.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H


static FT_Face g_tFace;
static int g_iDefaultFontSize = 12;

static int FreeTypeFontInit(char *aFineName)
{
    FT_Library    library;
    int error;

    error = FT_Init_FreeType( &library );                 /* initialize library */    
	if (error)
	{
		printf("FT_Init_FreeType err\n");
		return -1;
	}
	
    error = FT_New_Face(library, aFineName, 0, &g_tFace ); /* create face object */
	if (error)
	{
		printf("FT_New_Face err\n");
		return -1;
	}

    FT_Set_Pixel_Sizes(g_tFace, g_iDefaultFontSize, 0);

	return 0;
}

static int FreeTypeSetFontSize(int iFontSize)
{
    FT_Set_Pixel_Sizes(g_tFace, iFontSize, 0);
	return 0;
}

static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
	int error;
    FT_Vector pen;
    FT_Glyph  glyph;
    

    pen.x = ptFontBitMap->iCurOriginX * 64; /* 单位: 1/64像素 */
    pen.y = ptFontBitMap->iCurOriginY * 64; /* 单位: 1/64像素 */

	/* 转换:transformation */
	FT_Set_Transform(g_tFace, 0, &pen);

	/* 加载位图: load glyph image into the slot (erase previous one) */
	error = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER);
	if (error)
	{
		printf("FT_Load_Char error\n");
		return -1;
	}

	ptFontBitMap->pucBuffer = slot->bitmap.buffer;
	
	ptFontBitMap->tRegion.iLeftUpX = slot->bitmap_left;
	ptFontBitMap->tRegion.iLeftUpY = ptFontBitMap->iCurOriginY*2 - slot->bitmap_top;
	ptFontBitMap->tRegion.iWidth   = slot->bitmap.width;
	ptFontBitMap->tRegion.iHeigh   = slot->bitmap.rows;
	ptFontBitMap->iNextOriginX = ptFontBitMap->iCurOriginX + slot->advance.x / 64;
	ptFontBitMap->iNextOriginY = ptFontBitMap->iCurOriginY;

	return 0;
}


static FontOpr g_tFreetypeOpr = {
	.name          = "freetype",
	.FontInit      = FreeTypeFontInit,
	.SetFontSize   = FreeTypeSetFontSize,
	.GetFontBitMap = FreeTypeGetFontBitMap,
};

void FreetypeRegister(void)
{
	RegisterFont(&g_tFreetypeOpr);
}

static FontOpr g_tFreetypeOpr

第86~91行:实现FontOpr这个结构体里面的函数

        第87行:之后可以通过freetype这个名字找到FontOpr这个结构体

        第88行:初始化函数

        第89行:自定义设置字符大小

        第90行:得到位图并且设置坐标值

第19行:对应字体文件 

第20行:对应字体大小

static int FreeTypeFontInit(char *aFineName)

第22~44行:进行初始化 

        第27行:初始化freetype库

        第34行:加载字体文件

        第41行:设置默认的字体大小

static int FreeTypeSetFontSize(int iFontSize)

第 46~50行:自定义设置字符大小

给一个字符的编码值如何得到它的位图呢? 

static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)

第52~83行:得到位图并且设置坐标值

        第55行:pen用来反推原点

        第57行:记录点阵

        第59、60行:根据位图原点的X和Y坐标反推原点

        第63行:转换

        第66行:加载位图

        第73行:存放点阵

        第75~80行:描述一个文字的位图

void FreetypeRegister(void)

第93~96行: 进行注册,上层font_manager.c提供

 三、文字管理

        我们可能要用到的字体有多种,那么怎么选择用哪个字符呢,所以我们要编写一个程序管理多种字符。

font_manager.c


#include <font_manager.h>
#include <string.h>


static PFontOpr g_ptFonts = NULL;
static PFontOpr g_ptDefaulFontOpr = NULL;


void RegisterFont(PFontOpr ptFontOpr)
{
	ptFontOpr->ptNext = g_ptFonts;
	g_ptFonts = ptFontOpr;
}

void FontsRegister(void)
{
	extern void FreetypeRegister(void);
	FreetypeRegister();
}

int SelectAndInitFont(char *aFontOprName, char *aFontFileName)
{
	PFontOpr ptTmp = g_ptFonts;
	while (ptTmp)
	{
		if (strcmp(ptTmp->name, aFontOprName) == 0)
			break;
		ptTmp = ptTmp->ptNext;
	}

	if (!ptTmp)
		return -1;

	g_ptDefaulFontOpr = ptTmp;
	return ptTmp->FontInit(aFontFileName);
}

int SetFontSize(int iFontSize)
{
	return g_ptDefaulFontOpr->SetFontSize(iFontSize);
}

int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{
	return g_ptDefaulFontOpr->GetFontBitMap(dwCode, ptFontBitMap);
}


实现这四个函数

第6行: 注册的单个字库链表头放到g_ptFonts

第7行:将22~37行找到的字库名字保存在g_ptDefaulFontOpr

void RegisterFont(PFontOpr ptFontOpr)

第10~14行:实现与freetype.c链接的链表

void FontsRegister(void)

第16~20行:实现多个字体的注册

        第18行:调用底层代码提供一个注册函数,注册单个字库,从链表里挑出想要的字库

假设你有多个字库,你要选择某一个,要提供一个SelectAndInitFont字体文件

int SelectAndInitFont(char *aFontOprName, char *aFontFileName)

 第22~37行:首先在g_ptFonts链表里面挑出来你想要的字体文件

        aFontOprName表示字库操作的名字,aFontFileName表示字体文件的名字

        第24行:在链表里面挑出来

        第27行:表示已经找到了

        第29行:没有找到,继续在链表里的下一个位置找

        第32行:表示不存在

        第35行:如果存在则调用初始化函数

int SetFontSize(int iFontSize)

第39~42行:将找到的字体设置字体大小

int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)

 第44~47行:将找到的字体设置位图

四、单元测试 

1.font_test.c 

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <font_manager.h>
#include <disp_manager.h>

#define FONTDATAMAX 4096

static const unsigned char fontdata_8x16[FONTDATAMAX] = {
	//此处点阵省略
    .........

};

/**********************************************************************
 * 函数名称: lcd_put_ascii
 * 功能描述: 在LCD指定位置上显示一个8*16的字符
 * 输入参数: x坐标,y坐标,ascii码
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/ 
void lcd_put_ascii(int x, int y, unsigned char c)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
	int i, b;
	unsigned char byte;

	for (i = 0; i < 16; i++)
	{
		byte = dots[i];
		for (b = 7; b >= 0; b--)
		{
			if (byte & (1<<b))
			{
				/* show */
				PutPixel(x+7-b, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				PutPixel(x+7-b, y+i, 0); /* 黑 */
			}
		}
	}
}

int main(int argc, char **argv)
{

	PDispBuff ptBuffer;
	int error;

	FontBitMap tFontBitMap;
	char *str= "Hello Linux";
	int i = 0;
	int lcd_x;
	int lcd_y;
	int font_size;
		

	if (argc != 5)
	{
		printf("Usage: %s <font_file> <lcd_x> <lcd_y> <font_size>\n", argv[0]);
		return -1;
	}

	lcd_x = strtol(argv[2], NULL, 0);
	lcd_y = strtol(argv[3], NULL, 0);
	
	font_size  = strtol(argv[4], NULL, 0);
	
		
	DisplayInit();

	SelectDefaultDisplay("fb");

	InitDefaultDisplay();

	ptBuffer = GetDisplayBuffer();

	FontsRegister();
	
	error = SelectAndInitFont("freetype", argv[1]);
	if (error)
	{
		printf("SelectAndInitFont err\n");
		return -1;
	}
	
	SetFontSize(font_size);

	while (str[i])
	{
		/* get bitmap */
		tFontBitMap.iCurOriginX = lcd_x;
		tFontBitMap.iCurOriginY = lcd_y;
		error = GetFontBitMap(str[i], &tFontBitMap);
		if (error)
		{
			printf("SelectAndInitFont err\n");
			return -1;
		}

		/* draw on buffer */		
		DrawFontBitMap(&tFontBitMap,0xff0000);//0xff0000为红色

		/* flush to lcd/web */		
		FlushDisplayRegion(&tFontBitMap.tRegion, ptBuffer);
		

		lcd_x = tFontBitMap.iNextOriginX;
		lcd_y = tFontBitMap.iNextOriginY;	
		i++;
	}
	
	return 0;	
}


第60行:保存字符位图

第61行:想要显示的字符串

第68~72行:打印出具体用法    

                     <font_file>                字体文件

                     <lcd_x> <lcd_y>      显示的位置  

                     <font_size>              字体的大小

第74、75行:将字符串转化成整数传入到lcd_xlcd_y

第77行:将字符串转化成整数传入到 font_size

第86行:获得显示的设备

第88行:FontsRegister()注册单个字符库 ,从链表里挑出想要的字库

第90行:SelectAndInitFont("freetype", argv[1])去选择freetype字库操作,传入argv[1]字体文件

第97行:SetFontSize设置字体大小

第102、103行:绘制图像的原点 

第104行:GetFontBitMap得到字符位图

第112行:把位图绘制出来,绘制到默认设备中

第115行:绘制的内容刷到LCD或者web上

第118、119行:绘制完后取下一个位图的坐标

2.disp_manager.c

disp_manager.c中实现位图绘制

void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)

void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)
{
    int i, j, p, q;
	int x = ptFontBitMap->tRegion.iLeftUpX;
	int y = ptFontBitMap->tRegion.iLeftUpY;
    int x_max = x + ptFontBitMap->tRegion.iWidth;
    int y_max = y + ptFontBitMap->tRegion.iHeigh;
	int width = ptFontBitMap->tRegion.iWidth;
	unsigned char *buffer = ptFontBitMap->pucBuffer;

    //printf("x = %d, y = %d\n", x, y);

    for ( j = y, q = 0; j < y_max; j++, q++ )
    {
        for ( i = x, p = 0; i < x_max; i++, p++ )
        {
            if ( i < 0      || j < 0       ||
                i >= g_tDispBuff.iXres || j >= g_tDispBuff.iYres )
            continue;

            //image[j][i] |= bitmap->buffer[q * bitmap->width + p];
            if (buffer[q * width + p])
	            PutPixel(i, j, dwColor);
        }
    }
	
}

3.disp_manager.h 

声明函数DrawFontBitMap

void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor);

头文件包含#include <font_manager.h>

#ifndef _DISP_MANAGER_H
#define _DISP_MANAGER_H

#include <common.h>
#include <font_manager.h>


typedef struct DispBuff {
	int iXres;
	int iYres;
	int iBpp;
	char *buff;
}DispBuff, *PDispBuff;



typedef struct DispOpr {
	char *name;
	int (*DeviceInit)(void);
	int (*DeviceExit)(void);
	int (*GetBuffer)(PDispBuff ptDispBuff);
	int (*FlushRegion)(PRegion ptRegion, PDispBuff ptDispBuff);
	struct DispOpr *ptNext;
}DispOpr, *PDispOpr;

void RegisterDisplay(PDispOpr ptDispOpr);

void DisplayInit(void);
int SelectDefaultDisplay(char *name);
int InitDefaultDisplay(void);
int PutPixel(int x, int y, unsigned int dwColor);
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff);
PDispBuff GetDisplayBuffer(void);
void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor);


#endif

4. common.h

        因为显示系统和文字系统都需要用到这个区域结构体,所以我们直接把它拿出来定义成一个公共的头文件

#ifndef _COMMON_H
#define _COMMON_H

#ifndef NULL
#define NULL (void *)0
#endif

typedef struct Region {
	int iLeftUpX;
	int iLeftUpY;
	int iWidth;
	int iHeigh;
}Region, *PRegion;

#endif

5.unittest下的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := 

#obj-y += disp_test.o
#obj-y += input_test.o
obj-y += font_test.o

6.font下的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := 

obj-y += font_manager.o
obj-y += freetype.o

7.顶层的Makefile 


CROSS_COMPILE ?= 
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm

STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump

export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP

CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include

LDFLAGS := -lts -lpthread -lfreetype

export CFLAGS LDFLAGS

TOPDIR := $(shell pwd)
export TOPDIR

TARGET := test


obj-y += display/
obj-y += input/
obj-y += font/
obj-y += unittest/

all : start_recursive_build $(TARGET)
	@echo $(TARGET) has been built!

start_recursive_build:
	make -C ./ -f $(TOPDIR)/Makefile.build

$(TARGET) : built-in.o
	$(CC) -o $(TARGET) built-in.o $(LDFLAGS)

clean:
	rm -f $(shell find -name "*.o")
	rm -f $(TARGET)

distclean:
	rm -f $(shell find -name "*.o")
	rm -f $(shell find -name "*.d")
	rm -f $(TARGET)

 第20行:链接库

LDFLAGS := -lts -lpthread -lfreetype

 第30~33行:打开这四个文件夹进行make

obj-y += display/
obj-y += input/
obj-y += font/
obj-y += unittest/

五、上板测试

book@100ask:17_font_unittest_ok$ make
book@100ask:17_font_unittest_ok$ cp -r 17_font_unittest_ok/ ~/nfs_rootfs/
[root@100ask:~]#  mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
[root@100ask:~]# cd /mnt/
[root@100ask:/mnt/17_font_unittest_ok]# ./test ./simsun.ttc 100 400 100

注意:编译时候要将字库文件一起上传上去

运行效果:

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

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

相关文章

数字图像处理(实践篇) 十六 基于分水岭算法的图像分割

目录 一 分水岭算法 二 利用OpenCV实现分水岭算法的过程 三 实践 一 分水岭算法 基于任何灰度图像都可以视为地形表面&#xff0c;其中高强度表示山峰和山丘&#xff0c;而低强度表示山谷。首先&#xff0c;开始用不同颜色的水&#xff08;标签&#xff09;填充每个孤立的山…

循环单向链表(详解)

循环单向链表原理 循环单项链表项目结构 头文件RecycleLinlList.h 头文件具体代码 #ifndef RECYCLRLINKLIST #define RECYCLRLINKLIST#include <stdio.h> #include <stdlib.h>// 宏定义 #define CIRCLELINKLIST_TRUE 1 #define CIRCLELINKLIST_FALSE 0 // 链表…

7.2 Windows驱动开发:内核注册并监控对象回调

在笔者上一篇文章《内核枚举进程与线程ObCall回调》简单介绍了如何枚举系统中已经存在的进程与线程回调&#xff0c;本章LyShark将通过对象回调实现对进程线程的句柄监控&#xff0c;在内核中提供了ObRegisterCallbacks回调&#xff0c;使用这个内核回调函数&#xff0c;可注册…

vscode如何在没有网络的情况下安装插件

vscode如何在没有网络的情况下安装插件 start 遇到没有网络的电脑&#xff0c;无法直接从插件市场安装vscode的插件。写一下 vscode 插件离线安装的方法. 解决方案 目标电脑没有可以安装插件的网络&#xff0c;那我们只能在有网络的环境下载好我们的插件。然后拷贝软件到无…

localStorage 和sessionStorage

localStorage 和 sessionStorage 是浏览器提供的两种客户端存储数据的方式&#xff1a; 生命周期&#xff1a; localStorage&#xff1a; 存储在 localStorage 中的数据在浏览器关闭后仍然保留&#xff0c;直到被显式删除或浏览器清除缓存。sessionStorage&#xff1a; 存储在 …

【零基础入门Docker】Dockerfile中的USER指令以及dockerfile命令详解

✍面向读者&#xff1a;所有人 ✍所属专栏&#xff1a;Docker零基础入门专栏 目录 第 1 步&#xff1a;创建 Dockerfile 第 2 步&#xff1a;构建 Docker 镜像 第 3 步&#xff1a;运行 Docker 容器 第 4 步&#xff1a;验证输出 dockerfile命令详解 最佳实践 默认情况下…

对el-select封装成组件使用

效果与直接使用el-select一样&#xff0c;多处用el-select显得代码冗余就进行了封装 效果图&#xff1a; el-select封装&#xff1a; <template><div class"my-select"><el-selectv-model"person.modelValue":placeholder"placehold…

【广州华锐视点】机械零件拆装VR仿真教学系统

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐走进我们的生活。在教育领域&#xff0c;VR技术的应用也日益广泛&#xff0c;为学生提供了更加生动、直观的学习体验。广州华锐视点开发的机械零件拆装VR仿真教学系统作为一种新兴的教学方式&…

oops-framework框架 之 初始了解(一)

引擎&#xff1a;CocosCreator 环境&#xff1a; Mac Gitee: oops-framework 简介 oops-framework是由作者dgflash编写&#xff0c;基于CocosCreator 3.x而实现的开源框架。 该框架以插件形式存在&#xff0c;主要目的是为了降低与项目的耦合&#xff0c;并且通过插件内部的…

【代码】基于麻雀搜索优化Kmeans图像分割算法

程序名称&#xff1a;基于麻雀搜索优化Kmeans图像分割算法 实现平台&#xff1a;matlab 代码简介&#xff1a;首先使用麻雀搜索优化算法来确定 K-means 算法的初始质心位置&#xff0c;然后进行传统的 K-means 聚类。这样做的目的是为了避免 K-means 算法陷入局部最优解&…

Mybatis 的操作(续集)

Mybatis 是一款优秀的 持久性 框架,用于简化 JDBC 的开发 持久层 : 指的就是持久化操作的层,通常指数据访问层(dao),是用来操作数据库的 简单来说 Mybatis 是更简单完成程序和数据库交互的框架 Mybatis 的写法有两种 : 1.xml 2.注解 这两者各有利弊,后面进行总结 Mybati…

HTML5 的全局属性 hidden 和 display:none 的关系

目录 1&#xff0c;hidden 和 display:none 的关系2&#xff0c;其他隐藏元素的方式2.1&#xff0c;语意上的隐藏2.2&#xff0c;视觉上的隐藏 1&#xff0c;hidden 和 display:none 的关系 hidden - MDN 参考 一句话总结&#xff1a;hidden 是HTML5 新增的全局布尔属性&…

播放器开发(七):音视频同步实现

目录 学习课题&#xff1a;逐步构建开发播放器【QT5 FFmpeg6 SDL2】 原理 简单分析&#xff1a; 下图简单描述了在一个播放过程中&#xff0c;假设我们先播放音频&#xff0c;对比一个公共时间轴&#xff0c;视频就会始终比音频慢0.003s。 我们在日常中用一些播放器播放视频…

Oracle:左连接、右连接、全外连接、(+)号详解

目录 Oracle 左连接、右连接、全外连接、&#xff08;&#xff09;号详解 1、左外连接&#xff08;LEFT OUTER JOIN/ LEFT JOIN&#xff09; 2、右外连接&#xff08;RIGHT OUTER JOIN/RIGHT JOIN&#xff09; 3、全外连接&#xff08;FULL OUTER JOIN/FULL JOIN&#xff0…

013 OpenCV copyMakeBorder(padding)

目录 一、环境 二、原理 三、完整代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、原理 cv.copyMakeBorder是OpenCV库中的一个函数&#xff0c;用于在图像周围添加边框&#xff08;padding&#xff09;。这个函数可以用于图…

​iOS Class Guard github用法、工作原理和安装详解及使用经验总结

iOS Class Guard是一个用于OC类、协议、属性和方法名混淆的命令行工具。它是class-dump的扩展。这个工具会生成一个symbol table&#xff0c;这个table在编译期间会包含进工程中。iOS-Class-Guard能有效的隐藏绝大多数的类、协议、方法、属性和 实例变量 名。iOS-Class-Guard不…

Eclipse导入JavaWeb项目

Eclipse 导入JavaWeb项目教程 运行教程 亲爱的粉丝们&#xff0c;我深知你们对Eclipse导入Web项目教程的迫切需求。在这个充满竞争的时代&#xff0c;每一个项目都离不开高效的沟通。过程中需要对应的环境适配和软件安…

淘宝/天猫商品详情API接口丨京东商品详情丨1688商品详情丨接口key密钥获取方式

要获取淘宝/天猫商品详情API接口、京东商品详情API接口、1688商品详情API接口以及接口密钥&#xff08;Key&#xff09;&#xff0c;可以按照以下步骤进行操作&#xff1a; 注册并登录淘宝/天猫开发者中心或京东开放平台或1688开放平台&#xff0c;并创建应用。在创建应用的过…

cmake和vscode 下的cmake的使用详解(二)

第四讲&#xff1a; GDB 调试器 前言&#xff1a; GDB(GNU Debugger) 是一个用来 调试 C/C 程序 的功能强大的 调试器 &#xff0c;是 Linux 系统开发 C/C 最常用的调试器 程序员可以 使用 GDB 来跟踪程序中的错误 &#xff0c;从而减少程序员的工作量。 Linux 开发 C/C …

postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第36讲&#…