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
英文地址,請注明出處:http://en.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; 輸入玩家姓名通過輸入的上限值和下限值之間…