retro-go 1.45 编译及显示中文

        最近做了个使用 retro-go 的开源掌机 基于ESP32-S3的C19掌机(适配GBC外壳) - 立创开源硬件平台 ,做完后用提供的固件发现屏幕反显了,估计是屏幕型号不太对,随即自己拉 retro-go 官方库来编译,拉取的最新的 1.45 ,记录下适配的过程和解决的一些问题。

  1. 安装 esp-idf 5.2 ,retro-go 介绍可以用idf5.3 但我用5.3报错
  2. 拉取 retro-go ,在/components/retro-go/targets 下增加 一个target ,参考已经项目与自己硬件最接近版本,注意 key map 的设置格式
  3. 在/components/retro-go 下的 config.h 中 参考其它的target 将自己增加的target 引入进来

支持中文:

  1. 找一个中文的 ttf 可以用这个 https://github.com/TakWolf/fusion-pixel-font
  2. 用 font_converter.py 设置路径与字符范围及字体大小后生成c源文件
  3. 在 fonts.h 中增加生成的源文件内的变量名和设置枚举与 fonts 数组
  4. sdkconfig 中设置 CONFIG_FATFS_API_ENCODING_UTF_8=y 开启 fatfs 使用unicode 编码读取文件名

如果不开启 utf8 会导致 rg_utf8_get_codepoint 方法后台报 invalid utf-8 prefix,也就是无法正确得到文件名 。

参考:

Configuration Options Reference - ESP32-S3 - — ESP-IDF 编程指南 v5.5 文档

https://elm-chan.org/fsw/ff/doc/config.html#lfn_unicode

解决开启中文浏览列表会很卡的问题

        增加中文字体后且能成功显示中文字体后,会发现列表变得很卡,查看源码后知道其查询字库是一个个遍历的,字符数变多后肯定会卡,解决方法是字符数据定长或再加一个索引数组。不管哪种方法其本质都是希望能通过字符号+偏移量定位到具体字符数据,下面是主要代码:

typedef struct
{char name[16];uint8_t type;   // 0=monospace, 1=proportional ,2= location by mapuint8_t width;  // median width of glyphsuint8_t height; // height of tallest glyphsize_t  chars;  // glyph countconst uint32_t *map; //索引数组uint32_t map_len;//索引数组长度uint32_t map_start_code;//索引的第一个字符码uint8_t data[]; // stream of rg_font_glyph_t (end of list indicated by an entry with 0x0000 codepoint)
} rg_font_t;//rg_gui.c  get_glyph  增加 font->type == 2  的逻辑 小于等于255 直接查询,从map_start_code 开始使用索引
//   这只是方法的一部分
static size_t get_glyph(uint32_t *output, const rg_font_t *font, int points, int c)
{// Some glyphs are always zero widthif (!font || c == '\r' || c == '\n' || c == 0) // || c < 8 || c > 0xFFFF)return 0;if (points <= 0)points = font->height;const uint8_t *ptr = font->data;const rg_font_glyph_t *glyph = (rg_font_glyph_t *)ptr;if(font->type == 2){if (c <= 255){int times =0;while (glyph->code && glyph->code != c && times++ <=255){if (glyph->width != 0)ptr += (((glyph->width * glyph->height) - 1) / 8) + 1;ptr += sizeof(rg_font_glyph_t);glyph = (rg_font_glyph_t *)ptr;}}else if(c >= font->map_start_code){uint32_t map_index =  c - font->map_start_code;if (map_index < font->map_len){uint32_t data_index = font->map[map_index];glyph = (rg_font_glyph_t *)(ptr + data_index);}}}else{// for (size_t i = 0; i < font->chars && glyph->code && glyph->code != c; ++i)while (glyph->code && glyph->code != c){if (glyph->width != 0)ptr += (((glyph->width * glyph->height) - 1) / 8) + 1;ptr += sizeof(rg_font_glyph_t);glyph = (rg_font_glyph_t *)ptr;}}

 

        修改后的库:https://github.com/longxiangam/retro-go

       tools 下生成字库的 font_converter.py 脚本增加对索引的支持,使用这个脚本生成字库时选择生成 map 并设置 start code 生成的代码就会生成 索引数组。

from PIL import Image, ImageDraw, ImageFont
from tkinter import Tk, Label, Entry, StringVar, Button, Frame, Canvas, filedialog, ttk, Checkbutton, IntVar
import os
import re
import uuid################################ - Font format - ################################
#
# font:
# |
# ├── glyph_bitmap[] -> 8 bit array containing the bitmap data for all glyph
# |
# └── glyph_data[] -> struct that contains all the data to correctly draw the glyph
#
######################## - Explanation of glyph_bitmap[] - #######################
# First, let's see an example : '!'
#
# we are going to convert glyph_bitmap[] bytes to binary :
# 11111111,
# 11111111,
# 11000111,
# 11100000,
#
# then we rearrange them :
#  [3 bits wide]
#       111
#       111
#       111
# [9    111   We clearly reconize '!' character
# bits  111
# tall] 111
#       000
#       111
#       111
#       (000000)
#
# Second example with '0' :
# 0x30,0x04,0x07,0x09,0x00,0x07,
# 0x7D,0xFB,0xBF,0x7E,0xFD,0xFB,0xFF,0x7C,
#
# - width = 0x07 = 7
# - height = 0x09 = 9
# - data[n] = 0x7D,0xFB,0xBF,0x7E,0xFD,0xFB,0xFF,0x7C
#
# in binary :
# 1111101
# 11111011
# 10111111
# 1111110
# 11111101
# 11111011
# 11111111
# 1111100
#
# We see that everything is not aligned so we add zeros ON THE LEFT :
# ->01111101
#   11111011
#   10111111
# ->01111110
#   11111101
#   11111011
#   11111111
# ->01111100
#
# Next, we rearrange the bits :
#    [ 7 bits wide]
#       0111110
#       1111110
#       1110111
# [9    1110111
# bits  1110111     we can reconize '0' (if you squint a loooot)
# tall] 1110111
#       1110111
#       1111111
#       0111110
#       (0)
#
# And that's basically how characters are encoded using this tool# Example usage (defaults parameters)
list_char_ranges_init = "32-126, 160-255,19968-40959"
font_size_init = 12
map_start_code_init = "19968"  # Default map start codefont_path = ("arial.ttf")  # Replace with your TTF font path# Variables to track panning
start_x = 0
start_y = 0def get_char_list():list_char = []for intervals in list_char_ranges.get().split(','):first = intervals.split('-')[0]# we check if the user input is a single char or an intervaltry:second = intervals.split('-')[1]except IndexError:list_char.append(int(first))else:for char in range(int(first), int(second) + 1):list_char.append(char)return list_chardef find_bounding_box(image):pixels = image.load()width, height = image.sizex_min, y_min = width, heightx_max, y_max = 0, 0for y in range(height):for x in range(width):if pixels[x, y] >= 1:  # Looking for 'on' pixelsx_min = min(x_min, x)y_min = min(y_min, y)x_max = max(x_max, x)y_max = max(y_max, y)if x_min > x_max or y_min > y_max:  # No target pixels foundreturn Nonereturn (x_min, y_min, x_max+1, y_max+1)def load_ttf_font(font_path, font_size):# Load the TTF fontenforce_font_size = enforce_font_size_bool.get()pil_font = ImageFont.truetype(font_path, font_size)font_name = ' '.join(pil_font.getname())font_data = []for char_code in get_char_list():char = chr(char_code)image = Image.new("1", (font_size * 2, font_size * 2), 0) # generate mono bmp, 0 = black colordraw = ImageDraw.Draw(image)# Draw at pos 1 otherwise some glyphs are clipped. we remove the added offset belowdraw.text((1, 0), char, font=pil_font, fill=255)bbox = find_bounding_box(image)  # Get bounding boxif bbox is None: # control character / spacewidth, height = 0, 0offset_x, offset_y = 0, 0else:x0, y0, x1, y1 = bboxwidth, height = x1 - x0, y1 - y0offset_x, offset_y = x0, y0if offset_x:offset_x -= 1try: # Get the real glyph width including padding on the right that the box will removeadv_w = int(draw.textlength(char, font=pil_font))adv_w = max(adv_w, width + offset_x)except:adv_w = width + offset_x# Shift or crop glyphs that would be drawn beyond font_size. Most glyphs are not affected by this.# If enforce_font_size is false, then max_height will be calculated at the end and the font might# be taller than requested.if enforce_font_size and offset_y + height > font_size:print(f"    font_size exceeded: {offset_y+height}")if font_size - height >= 0:offset_y = font_size - heightelse:offset_y = 0height = font_size# Extract bitmap datacropped_image = image.crop(bbox)bitmap = []row = 0i = 0for y in range(height):for x in range(width):if i == 8:bitmap.append(row)row = 0i = 0pixel = 1 if cropped_image.getpixel((x, y)) else 0row = (row << 1) | pixeli += 1bitmap.append(row << 8-i) # to "fill" with zero the remaining empty bitsbitmap = bitmap[0:int((width * height + 7) / 8)]# Create glyph entryglyph_data = {"char_code": char_code,"ofs_y": int(offset_y),"box_w": int(width),"box_h": int(height),"ofs_x": int(offset_x),"adv_w": int(adv_w),"bitmap": bitmap,}font_data.append(glyph_data)# The font render glyphs at font_size but they can shift them up or down which will cause the max_height# to exceed font_size. It's not desirable to remove the padding entirely (the "enforce" option above), # but there are some things we can do to reduce the discrepency without affecting the look.max_height = max(g["ofs_y"] + g["box_h"] for g in font_data)if max_height > font_size:min_ofs_y = min((g["ofs_y"] if g["box_h"] > 0 else 1000) for g in font_data)for key, glyph in enumerate(font_data):offset = glyph["ofs_y"]# If there's a consistent excess of top padding across all glyphs, we can remove itif min_ofs_y > 0 and offset >= min_ofs_y:offset -= min_ofs_y# In some fonts like Vera and DejaVu we can shift _ and | to gain an extra pixelif chr(glyph["char_code"]) in ["_", "|"] and offset + glyph["box_h"] > font_size and offset > 0:offset -= 1font_data[key]["ofs_y"] = offsetmax_height = max(g["ofs_y"] + g["box_h"] for g in font_data)print(f"Glyphs: {len(font_data)}, font_size: {font_size}, max_height: {max_height}")return (font_name, font_size, font_data)def load_c_font(file_path):# Load the C fontfont_name = "Unknown"font_size = 0font_data = []with open(file_path, 'r', encoding='UTF-8') as file:text = file.read()text = re.sub('//.*?$|/\*.*?\*/', '', text, flags=re.S|re.MULTILINE)text = re.sub('[\n\r\t\s]+', ' ', text)# FIXME: Handle parse errors...if m := re.search('\.name\s*=\s*"(.+)",', text):font_name = m.group(1)if m := re.search('\.height\s*=\s*(\d+),', text):font_size = int(m.group(1))if m := re.search('\.data\s*=\s*\{(.+?)\}', text):hexdata = [int(h, base=16) for h in re.findall('0x[0-9A-Fa-f]{2}', text)]while len(hexdata):char_code = hexdata[0] | (hexdata[1] << 8)if not char_code:breakofs_y = hexdata[2]box_w = hexdata[3]box_h = hexdata[4]ofs_x = hexdata[5]adv_w = hexdata[6]bitmap = hexdata[7:int((box_w * box_h + 7) / 8) + 7]glyph_data = {"char_code": char_code,"ofs_y": ofs_y,"box_w": box_w,"box_h": box_h,"ofs_x": ofs_x,"adv_w": adv_w,"bitmap": bitmap,}font_data.append(glyph_data)hexdata = hexdata[7 + len(bitmap):]return (font_name, font_size, font_data)def generate_font_data():if font_path.endswith(".c"):font_name, font_size, font_data = load_c_font(font_path)else:font_name, font_size, font_data = load_ttf_font(font_path, int(font_height_input.get()))window.title(f"Font preview: {font_name} {font_size}")font_height_input.set(font_size)max_height = max(font_size, max(g["ofs_y"] + g["box_h"] for g in font_data))bounding_box = bounding_box_bool.get()canvas.delete("all")offset_x_1 = 1offset_y_1 = 1for glyph_data in font_data:offset_y = glyph_data["ofs_y"]width = glyph_data["box_w"]height = glyph_data["box_h"]offset_x = glyph_data["ofs_x"]adv_w = glyph_data["adv_w"]if offset_x_1+adv_w+1 > canva_width:offset_x_1 = 1offset_y_1 += max_height + 1byte_index = 0byte_value = 0bit_index = 0for y in range(height):for x in range(width):if bit_index == 0:byte_value = glyph_data["bitmap"][byte_index]byte_index += 1if byte_value & (1 << 7-bit_index):canvas.create_rectangle((x+offset_x_1+offset_x)*p_size, (y+offset_y_1+offset_y)*p_size, (x+offset_x_1+offset_x)*p_size+p_size, (y+offset_y_1+offset_y)*p_size+p_size,fill="white")bit_index += 1bit_index %= 8if bounding_box:canvas.create_rectangle((offset_x_1+offset_x)*p_size, (offset_y_1+offset_y)*p_size, (width+offset_x_1+offset_x)*p_size, (height+offset_y_1+offset_y)*p_size, width=1, outline="red", fill='')canvas.create_rectangle((offset_x_1)*p_size, (offset_y_1)*p_size, (offset_x_1+adv_w)*p_size, (offset_y_1+max_height)*p_size, width=1, outline='blue', fill='')offset_x_1 += adv_w + 1return (font_name, font_size, font_data)def save_font_data():font_name, font_size, font_data = generate_font_data()filename = filedialog.asksaveasfilename(title='Save Font',initialdir=os.getcwd(),initialfile=f"{font_name.replace('-', '_').replace(' ', '')}{font_size}",defaultextension=".c",filetypes=(('Retro-Go Font', '*.c'), ('All files', '*.*')))if filename:with open(filename, 'w', encoding='UTF-8') as f:f.write(generate_c_font(font_name, font_size, font_data))def generate_c_font(font_name, font_size, font_data):normalized_name = f"{font_name.replace('-', '_').replace(' ', '')}{font_size}"max_height = max(font_size, max(g["ofs_y"] + g["box_h"] for g in font_data))memory_usage = sum(len(g["bitmap"]) + 7 for g in font_data)  # 7 bytes for header# Calculate map data if enabledgenerate_map = generate_map_bool.get()map_start_code = int(map_start_code_input.get()) if generate_map else 0map_data = []if generate_map:# Find the range for the mapchar_codes = [g["char_code"] for g in font_data]max_char = max(char_codes)map_size = max_char - map_start_code + 1map_data = [0] * map_size  # Initialize with zerosdata_index = 0for glyph in font_data:map_index = glyph["char_code"] - map_start_codeif 0 <= map_index < map_size:map_data[map_index] = data_indexdata_index += 7 + len(glyph["bitmap"])  # 7 bytes header + bitmap sizememory_usage += map_size * 4  # Each map entry is 4 bytes (uint32_t)file_data = "#include \"../rg_gui.h\"\n\n"file_data += "// File generated with font_converter.py (https://github.com/ducalex/retro-go/tree/dev/tools)\n\n"file_data += f"// Font           : {font_name}\n"file_data += f"// Point Size     : {font_size}\n"file_data += f"// Memory usage   : {memory_usage} bytes\n"file_data += f"// # characters   : {len(font_data)}\n"if generate_map:file_data += f"// Map start code : {map_start_code}\n"file_data += f"// Map size       : {len(map_data)} entries\n"file_data += "\n"font_type = 1;if generate_map:file_data += f"static const uint32_t font_{normalized_name}_map[] = {{\n"for i in range(0, len(map_data), 8):line = map_data[i:i+8]file_data += "    " + ", ".join([f"0x{val:04X}" for val in line]) + ",\n"file_data += "};\n\n"font_type = 2;file_data += f"const rg_font_t font_{normalized_name} = {{\n"file_data += f"    .name = \"{font_name}\",\n"file_data += f"    .type = {font_type},\n"file_data += f"    .width = 0,\n"file_data += f"    .height = {max_height},\n"file_data += f"    .chars = {len(font_data)},\n"if generate_map:file_data += f"    .map_start_code = {map_start_code},\n"file_data += f"    .map = font_{normalized_name}_map,\n"file_data += f"    .map_len = sizeof(font_{normalized_name}_map) / 4,\n"file_data += f"    .data = {{\n"for glyph in font_data:char_code = glyph['char_code']header_data = [char_code & 0xFF, char_code >> 8, glyph['ofs_y'], glyph['box_w'],glyph['box_h'], glyph['ofs_x'], glyph['adv_w']]file_data += f"        /* U+{char_code:04X} '{chr(char_code)}' */\n        "file_data += ", ".join([f"0x{byte:02X}" for byte in header_data])file_data += f",\n        "if len(glyph["bitmap"]) > 0:file_data += ", ".join([f"0x{byte:02X}" for byte in glyph["bitmap"]])file_data += f","file_data += "\n"file_data += "\n"file_data += "        // Terminator\n"file_data += "        0x00, 0x00,\n"file_data += "    },\n"file_data += "};\n"return file_datadef select_file():filename = filedialog.askopenfilename(title='Load Font',initialdir=os.getcwd(),filetypes=(('True Type Font', '*.ttf'), ('Retro-Go Font', '*.c'), ('All files', '*.*')))if filename:global font_pathfont_path = filenamegenerate_font_data()# Function to zoom in and out on the canvas
def zoom(event):scale = 1.0if event.delta > 0:  # Scroll up to zoom inscale = 1.2elif event.delta < 0:  # Scroll down to zoom outscale = 0.8# Get the canvas size and adjust scale based on cursor positioncanvas.scale("all", event.x, event.y, scale, scale)# Update the scroll region to reflect the new scalecanvas.configure(scrollregion=canvas.bbox("all"))def start_pan(event):global start_x, start_y# Record the current mouse positionstart_x = event.xstart_y = event.ydef pan_canvas(event):global start_x, start_y# Calculate the distance moveddx = start_x - event.xdy = start_y - event.y# Scroll the canvascanvas.move("all", -dx, -dy)# Update the starting positionstart_x = event.xstart_y = event.yif __name__ == "__main__":window = Tk()window.title("Retro-Go Font Converter")# Get screen width and heightscreen_width = window.winfo_screenwidth()screen_height = window.winfo_screenheight()# Set the window size to fill the entire screenwindow.geometry(f"{screen_width}x{screen_height}")p_size = 8 # pixel size on the renderercanva_width = screen_width//p_sizecanva_height = screen_height//p_size-16frame = Frame(window)frame.pack(anchor="center", padx=10, pady=2)# choose font button (file picker)choose_font_button = ttk.Button(frame, text='Choose font', command=select_file)choose_font_button.pack(side="left", padx=5)# Label and Entry for Font heightLabel(frame, text="Font height").pack(side="left", padx=5)font_height_input = StringVar(value=str(font_size_init))Entry(frame, textvariable=font_height_input, width=4).pack(side="left", padx=5)# Variable to hold the state of the checkboxenforce_font_size_bool = IntVar()  # 0 for unchecked, 1 for checkedCheckbutton(frame, text="Enforce size", variable=enforce_font_size_bool).pack(side="left", padx=5)# Label and Entry for Char ranges to includeLabel(frame, text="Ranges to include").pack(side="left", padx=5)list_char_ranges = StringVar(value=str(list_char_ranges_init))Entry(frame, textvariable=list_char_ranges, width=30).pack(side="left", padx=5)# Variable to hold the state of the checkboxbounding_box_bool = IntVar(value=1)  # 0 for unchecked, 1 for checkedCheckbutton(frame, text="Bounding box", variable=bounding_box_bool).pack(side="left", padx=10)# Variable to hold the state of the map generation checkboxgenerate_map_bool = IntVar()  # 0 for unchecked, 1 for checkedCheckbutton(frame, text="Generate map", variable=generate_map_bool).pack(side="left", padx=5)# Label and Entry for Map start codeLabel(frame, text="Map start code").pack(side="left", padx=5)map_start_code_input = StringVar(value=str(map_start_code_init))Entry(frame, textvariable=map_start_code_input, width=6).pack(side="left", padx=5)# Button to launch the font generation functionb1 = Button(frame, text="Preview", width=14, height=2, background="blue", foreground="white", command=generate_font_data)b1.pack(side="left", padx=5)# Button to launch the font exporting functionb1 = Button(frame, text="Save", width=14, height=2, background="blue", foreground="white", command=save_font_data)b1.pack(side="left", padx=5)frame = Frame(window).pack(anchor="w", padx=2, pady=2)canvas = Canvas(frame, width=canva_width*p_size, height=canva_height*p_size, bg="black")canvas.configure(scrollregion=(0, 0, canva_width*p_size, canva_height*p_size))canvas.bind("<MouseWheel>", zoom)canvas.bind("<ButtonPress-1>", start_pan)  # Start panningcanvas.bind("<B1-Motion>",pan_canvas)canvas.focus_set()canvas.pack(fill="both", expand=True)window.mainloop()

 

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

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

相关文章

中州养老项目:Mybatis自动填充拦截器

功能:在新增护理项目的时候,创建人,创建时间和修改时间字段会自动拦截填充,这些公共字段可以省去我们一个一个处理的麻烦依靠:AutoFillInterceptor拦截器,MybatisConfig配置类第一步:我们需要借助一个MybatisConfig,configuration标志着这是一个配置类,我们需要将autoFillInter…

[创业之路-527]:什么是产品技术成熟度曲线?

产品技术成熟度曲线&#xff08;Gartner Hype Cycle&#xff09;是由全球知名咨询机构Gartner提出的工具&#xff0c;用于可视化展示新兴技术从诞生到成熟的发展轨迹&#xff0c;以及市场对其预期和实际采用趋势的变化。该曲线通过五个阶段刻画技术生命周期&#xff0c;帮助企业…

VScode对Ubuntu用root账号进行SSH远程连接开发

由于linux服务器大部分都是基于命令行的操作&#xff0c;缺乏比较方便好用的编辑工具&#xff0c;对于经常在linux服务器上做开发的同学来说直接在服务器上进行开发或配置文件的修改还不是特别的方便。虽然linux上有vi或vim比起图形化的编辑工具体验感还是不是很好。作为程序员…

【物联网】基于树莓派的物联网开发【20】——树莓派控制DHT11温湿度传感器实战

传感器概述 DHT11是一款有已校准数字信号输出的温湿度传感器。 其精度湿度5%RH&#xff0c; 温度2℃&#xff0c;量程湿度20-90%RH&#xff0c; 温度0~50℃。分为3个接口&#xff0c;分别为&#xff1a;VCC, DATA, GND。 产品图片主要用途 检测环境温湿度 GPIO控制DHT11温湿度传…

AI原生数据库:告别SQL的新时代来了?

在2025年的今天&#xff0c;生成式AI的浪潮正以前所未有的力量重塑着各行各业。从代码生成到艺术创作&#xff0c;大型语言模型&#xff08;LLM&#xff09;的能力边界不断被拓宽。现在&#xff0c;这股浪潮正涌向信息技术领域最古老、最核心的基石之一&#xff1a;数据库。一个…

题单【模拟与高精度】

P1042 [NOIP 2003 普及组] 乒乓球 P1042 [NOIP 2003 普及组] 乒乓球 - 洛谷 #include<bits/stdc.h> using namespace std;char C; string S; int n,A,B;void Work(int Lim) {for(char i:S){if(iW) A;if(iL) B;if(max(A,B)>Lim && abs(A-B)>2){cout<<…

数据结构学习基础和从包装类缓存到泛型擦除的避坑指南

目录 1.数据结构的概念和算法 1.1 数据结构的概念 1.2 数据结构的集合框架 1.3 算法 1.3.1 时间复杂度 1.3.2 空间复杂度 2.包装类 2.1 为什么需要包装类&#xff1f; 2.2 装箱和拆箱 3. 初识泛型 3.1 认识泛型 3.2 泛型类的使用 3.3 泛型的编译 3.4 通配符 3.4.1 …

网络安全基础知识【6】

什么是防火墙1.防火墙指的是一个由软件和硬件设备组合而成、在内部网和外部网之间、 专用网与公共网之间的界面上构造的保护屏障 2.防火墙实际上是一种隔离技术 3.防火墙重要的特征是增加了区域的概念防火墙的定义 隔离可信与不可信网络的设备/软件&#xff0c;基于策略控制流量…

Apache Doris数据库——大数据技术

Apache Doris一、简介1.1、Apache Doris简介1.2、Apache Doris 与传统大数据架构相比1.3、doris是java团队掌控大数据能力最优选择1.4、 OLTP&#xff08;在线事务处理&#xff09; 与 OLAP&#xff08;在线分析处理&#xff09;1.5、发展历程1.6、应用现状1.7、整体架构1.7.1、…

Conda和pip的使用记录

Conda和pip的使用记录一、创建新的 Conda 环境二、激活环境三、安装其他包&#xff08;可选&#xff09;四、查看已有环境五、删除环境&#xff08;可选&#xff09;⚙️ Conda 下载缓慢的解决方案&#xff08;推荐使用国内镜像&#xff09;&#x1f527; 方法一&#xff1a;**…

详解Python标准库之互联网数据处理

详解Python标准库之互联网数据处理 在互联网时代&#xff0c;数据的产生、传输和处理无处不在。从电子邮件的收发到 API 接口的数据交换&#xff0c;从二进制数据的编码到 MIME 类型的识别&#xff0c;Python 标准库提供了一整套强大的工具集&#xff0c;帮助开发者轻松应对各种…

适 配 器 模 式

前阵子&#xff0c;笔者在网上淘来一个二手显示屏来搭配我装好的主机&#xff0c;但是送到手上后我却找不到电源适配器的踪迹。于是我就在家找了根电源线接上了显示屏&#xff0c;倒是能亮&#xff0c;就是屏幕闪得和机关枪似的。这是因为我的显示屏需要12V的供电&#xff0c;我…

智慧零售商品识别准确率↑32%:陌讯多模态融合算法实战解析

原创声明本文为原创技术解析&#xff0c;核心技术参数与架构设计引用自《陌讯技术白皮书》&#xff0c;禁止任何形式的未经授权转载。一、行业痛点&#xff1a;智慧零售的 "看得见的障碍"在智慧零售场景中&#xff0c;从自助结算终端到智能货架管理&#xff0c;计算机…

Linux系统编程-gcc(黑马笔记)

1 gcc的编译流程gcc编译的整个过程并且整个过程下来的每个过程。并且给出了每个阶段产物和gcc命令。1.1 数据段合并其实就是因为“块” 一次是读多个字节而不是一个字节&#xff0c;所以会将一些地址段合并从而提升效率1.2 地址回填这张图也有些问题&#xff0c;正确的结论是:地…

Git踩坑

文章目录前言❓问题分析&#xff1a;为什么你的提交会“覆盖”别人的代码&#xff1f;✅ 正确的代码提交流程&#xff08;结合你原文的说明&#xff09;**1. 确认自己在正确的分支上****2. 从主开发分支&#xff08;如 dev&#xff09;拉取最新代码并合并****3. 解决冲突&#…

sqli-labs:Less-20关卡详细解析

1. 思路&#x1f680; 本关的SQL语句为&#xff1a; $sql"SELECT * FROM users WHERE username$cookee LIMIT 0,1";注入类型&#xff1a;字符串型&#xff08;单引号包裹&#xff09;、GET操作提示&#xff1a;参数需以闭合关键参数&#xff1a;cookee php输出语句…

基于LevitUnet的超声图像分割

完整项目包获取&#xff1a;点击文末名片本项目旨在开发一个基于深度学习的图像分割模型&#xff0c;专门用于处理医学或遥感领域的图像数据&#xff08;以 TIFF 格式存储&#xff09;。通过结合 LeViT&#xff08;基于 Vision Transformer 的轻量模型&#xff09;和 U-Net 架构…

Java 17 新特性解析与代码示例

Java 17 新特性解析与代码示例 文章目录Java 17 新特性解析与代码示例引言1. 密封类&#xff08;JEP 409&#xff09;1.1. 介绍1.2. 详细说明1.3. 代码示例1.4. 与之前功能的对比1.5. 使用场景1.6. 总结2. switch 模式匹配&#xff08;预览&#xff0c;JEP 406&#xff09;2.1.…

SQL中的GROUP BY用法

GROUP BY 是 SQL 中用来“按列分组”的子句。 它把相同值的行分到同一个组&#xff0c;然后通常配合聚合函数&#xff08;COUNT, SUM, AVG, MAX, MIN 等&#xff09;对每个组做统计&#xff0c;最终每组只返回一行结果。✅ 1. 基本语法 SELECT 列1, 列2, 聚合函数(列3) FROM 表…

AI Agent开发学习系列 - LangGraph(10): 带有循环的Looping Graph(练习解答)

在AI Agent开发学习系列 - LangGraph(9): 带有循环的Looping Graph中&#xff0c;我们学习了如何创建带有循环的Looping Graph。为了巩固学习&#xff0c;我们来做一个练习。 用LangGraph创建如下图的一个Agent: 要求&#xff1a; 输入玩家姓名通过输入的上限值和下限值之间…