Android使用OpenGL和FreeType绘制文字

Open GL主要是渲染图形的,有时候需要绘制文字,网上搜了一下,基本思路都是把文字转成位图,再使用Open GL纹理进行渲染。加载纹理在特定阶段才能成功(在onSurfaceCreated中加载),这样就无法动态的绘制字符串,一种方式是把可能用到的字符都加载到一个位图,渲染纹理的时候不同的字符就渲染纹理的特定区域,另一种方式就是每个字符生成一个位图(本文提供的代码就是这种方式)。

1、集成FreeType

这里我们直接使用源码集成 下载FreeType源码

新建一个 Android Native Library 类型的 Module 或者点击 File -> Add C++ to Module,下载的FreeType源码解压后文件夹改成 freetype,然后把整个文件夹复制到 cpp 目录,在 cpp 目录下的 CMakeLists.txt 中添加 freetype:

add_subdirectory(freetype)
target_link_libraries(${CMAKE_PROJECT_NAME}# List libraries link to the target libraryandroidlogfreetype)

我建的Module名称是 jfreetype,实现的代码主要有:

jfreetype.cpp

#include <jni.h>
#include <string>
#include <android//log.h>
#include "ft2build.h"
#include FT_FREETYPE_H#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, "NDK FT", __VA_ARGS__)
#define LOG_W(...) __android_log_print(ANDROID_LOG_WARN, "NDK FT", __VA_ARGS__)
#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR, "NDK FT", __VA_ARGS__)// https://freetype.org/freetype2/docs/tutorialFT_Library library;   /* handle to library     */
FT_Face face;      /* handle to face object */extern "C" JNIEXPORT jint JNICALL
Java_site_feiyuliuxing_jfreetype_JFreeType_init(JNIEnv *env,jobject, jobject face_buffer) {std::string hello = "Hello from C++";FT_Error error = FT_Init_FreeType(&library);if (error) {LOG_E("an error occurred during library initialization, error: %d", error);return error;}jbyte *buffer = (jbyte *) (env->GetDirectBufferAddress(face_buffer));jlong size = env->GetDirectBufferCapacity(face_buffer);error = FT_New_Memory_Face(library,(FT_Byte *) buffer,    /* first byte in memory */size,      /* size in bytes        */0,         /* face_index           */&face);if (error) {LOG_E("an error occurred during FT_New_Memory_Face, error: %d", error);return error;}error = FT_Set_Pixel_Sizes(face,   /* handle to face object */0,      /* pixel_width           */128);   /* pixel_height          */if (error) {LOG_E("an error occurred during FT_Set_Pixel_Sizes, error: %d", error);return error;}return 0;
}extern "C"
JNIEXPORT jint JNICALL
Java_site_feiyuliuxing_jfreetype_JFreeType_charBitmap(JNIEnv *env, jobject thiz,jobject ft_bitmap, jchar charcode) {FT_UInt glyph_index = FT_Get_Char_Index(face, charcode);FT_Error error = FT_Load_Glyph(face,          /* handle to face object */glyph_index,   /* glyph index           */FT_LOAD_DEFAULT);  /* load flags, see below */if (error) {LOG_E("an error occurred during FT_Get_Char_Index, error: %d", error);return error;}error = FT_Render_Glyph(face->glyph,   /* glyph slot  */FT_RENDER_MODE_NORMAL); /* render mode */if (error) {LOG_E("an error occurred during FT_Render_Glyph, error: %d", error);return error;}FT_Bitmap bitmap = face->glyph->bitmap;LOG_I("--------------- %c ---------------", charcode);LOG_I("FT_Bitmap size: %d x %d", bitmap.width, bitmap.rows);LOG_I("FT_Bitmap pixel mode: %d", bitmap.pixel_mode);LOG_I("FT_Bitmap bitmap top: %d", face->glyph->bitmap_top);LOG_I("metrics.height: %ld", face->glyph->metrics.height);LOG_I("metrics.horiBearingY: %ld", face->glyph->metrics.horiBearingY);jclass bmpCls = env->GetObjectClass(ft_bitmap);jfieldID rowsID = env->GetFieldID(bmpCls, "rows", "I");jfieldID widthID = env->GetFieldID(bmpCls, "width", "I");jfieldID bufferID = env->GetFieldID(bmpCls, "buffer", "[B");jfieldID leftID = env->GetFieldID(bmpCls, "bitmapLeft", "I");jfieldID topID = env->GetFieldID(bmpCls, "bitmapTop", "I");env->SetIntField(ft_bitmap, rowsID, (int) bitmap.rows);env->SetIntField(ft_bitmap, widthID, (int) bitmap.width);env->SetIntField(ft_bitmap, leftID, face->glyph->bitmap_left);env->SetIntField(ft_bitmap, topID, face->glyph->bitmap_top);int dataLength = bitmap.rows * bitmap.width;jbyteArray buf = env->NewByteArray(dataLength);jbyte *data = env->GetByteArrayElements(buf, nullptr);for (int i = 0; i < dataLength; ++i) {data[i] = bitmap.buffer[i];}env->ReleaseByteArrayElements(buf, data, 0);env->SetObjectField(ft_bitmap, bufferID, buf);return 0;
}extern "C"
JNIEXPORT void JNICALL
Java_site_feiyuliuxing_jfreetype_JFreeType_close(JNIEnv *env, jobject thiz) {FT_Done_FreeType(library);
}

FTBitmap.kt

import android.graphics.Bitmap
import android.graphics.Colorclass FTBitmap @JvmOverloads constructor(var rows: Int = 0,var width: Int = 0,var buffer: ByteArray? = null,var bitmapLeft: Int = 0,var bitmapTop: Int = 0,
) {fun toBitmap(maxAscent: Int, maxDescent: Int): Bitmap? {if (buffer == null) return nullval xOffset = bitmapLeftval yOffset = maxAscent - bitmapTopval width = this.width + xOffsetval height = rows + yOffset + maxDescent - (rows - bitmapTop)val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)for (y in 0 until rows) {for (x in 0 until this.width) {val index = y * this.width + xval pixelValue = buffer!![index].toInt() and 0xffbitmap.setPixel(x + xOffset, y + yOffset, Color.rgb(pixelValue, pixelValue, pixelValue))}}return bitmap}
}

 JFreeType.kt

package site.feiyuliuxing.jfreetypeimport java.nio.ByteBufferclass JFreeType {/*** A native method that is implemented by the 'jfreetype' native library,* which is packaged with this application.*/external fun init(faceBuffer: ByteBuffer): Intexternal fun charBitmap(ftBitmap: FTBitmap, char: Char): Intexternal fun close()companion object {// Used to load the 'jfreetype' library on application startup.init {System.loadLibrary("jfreetype")}}
}

至此,我们需要的接口都已经准备好啦,继续~~

2、使用Open GL绘制文字

Android Open GL基础这里就不介绍了,如有需要,可以参考构建OpenGL ES环境

需要准备一个字体文件,可以自己搜索下载一个ttf,替换后面代码中的“SourceCodePro-Regular.ttf”

GLUtil.kt

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.opengl.GLES11Ext.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
import android.opengl.GLES11Ext.GL_TEXTURE_MAX_ANISOTROPY_EXT
import android.opengl.GLES30.*
import android.opengl.GLUtils
import android.util.Log
import androidx.annotation.DrawableRes
import java.nio.ByteBufferobject GLUtil {private const val TAG = "GLUtil"fun createShaderProgram(vertexShaderSource: String, fragmentShaderSource: String): Int {val vShader = glCreateShader(GL_VERTEX_SHADER)val fShader = glCreateShader(GL_FRAGMENT_SHADER)glShaderSource(vShader, vertexShaderSource)glShaderSource(fShader, fragmentShaderSource)val status = IntArray(1)glCompileShader(vShader)checkOpenGLError()glGetShaderiv(vShader, GL_COMPILE_STATUS, status, 0)if (status[0] != 1) {Log.e(TAG, "vertex compilation failed")printShaderLog(vShader)}glCompileShader(fShader)checkOpenGLError()glGetShaderiv(fShader, GL_COMPILE_STATUS, status, 0)if (status[0] != 1) {Log.e(TAG, "fragment compilation failed")printShaderLog(fShader)}val vfProgram = glCreateProgram()glAttachShader(vfProgram, vShader)glAttachShader(vfProgram, fShader)glLinkProgram(vfProgram)checkOpenGLError()glGetProgramiv(vfProgram, GL_LINK_STATUS, status, 0)if (status[0] != 1) {Log.e(TAG, "linking failed")printProgramLog(vfProgram)}return vfProgram}private fun printShaderLog(shader: Int) {val len = IntArray(1)glGetShaderiv(shader, GL_INFO_LOG_LENGTH, len, 0)if (len[0] > 0) {val log = glGetShaderInfoLog(shader)Log.e(TAG, "Shader Info Log: $log")}}private fun printProgramLog(prog: Int) {val len = IntArray(1)glGetProgramiv(prog, GL_INFO_LOG_LENGTH, len, 0)Log.e(TAG, "printProgramLog() - log length=${len[0]}")if (len[0] > 0) {val log = glGetProgramInfoLog(prog)Log.e(TAG, "Program Info Log: $log")}}private fun checkOpenGLError(): Boolean {var foundError = falsevar glErr = glGetError()while (glErr != GL_NO_ERROR) {Log.e(TAG, "glError: $glErr")foundError = trueglErr = glGetError()}return foundError}fun Context.loadTexture(@DrawableRes img: Int): Int {val options = BitmapFactory.Options()options.inScaled = falseval bitmap = BitmapFactory.decodeResource(resources, img, options)return loadTexture(bitmap)}fun loadTexture(bitmap: Bitmap): Int {Log.d(TAG, "bitmap size: ${bitmap.width} x ${bitmap.height}")val textures = IntArray(1)glGenTextures(1, textures, 0)val textureID = textures[0]if (textureID == 0) {Log.e(TAG, "Could not generate a new OpenGL textureId object.")return 0}glBindTexture(GL_TEXTURE_2D, textureID)// https://developer.android.google.cn/reference/android/opengl/GLES20#glTexImage2D(int,%20int,%20int,%20int,%20int,%20int,%20int,%20int,%20java.nio.Buffer)/*      int target,int level,int internalformat,int width,int height,int border,int format,int type,Buffer pixels */val pixels = ByteBuffer.allocateDirect(bitmap.byteCount)bitmap.copyPixelsToBuffer(pixels)pixels.position(0)//这步比较关键,不然无法加载纹理数据val internalformat = GLUtils.getInternalFormat(bitmap)val type = GLUtils.getType(bitmap)
//        Log.i(TAG, "internalformat=$internalformat, GL_RGBA=$GL_RGBA")
//        Log.i(TAG, "type=$type, GL_UNSIGNED_BYTE=$GL_UNSIGNED_BYTE")
//        glTexImage2D(GL_TEXTURE_2D, 0, internalformat, bitmap.width, bitmap.height, 0, internalformat, type, pixels)GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0)glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)glGenerateMipmap(GL_TEXTURE_2D)val ext = glGetString(GL_EXTENSIONS)
//        Log.e(TAG, ext)if (ext.contains("GL_EXT_texture_filter_anisotropic")) {val anisoset = FloatArray(1)glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, anisoset, 0)Log.d(TAG, "anisoset=${anisoset[0]}")glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoset[0])}bitmap.recycle()return textureID}
}

GLChar.kt

import android.graphics.Bitmap
import android.opengl.GLES30.*
import java.nio.ByteBuffer
import java.nio.ByteOrderclass GLChar(bitmap: Bitmap) {private var positionVertex = FloatArray(15)private val vertexBuffer = ByteBuffer.allocateDirect(positionVertex.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(positionVertex).apply{ position(0) }private val texVertexBuffer = ByteBuffer.allocateDirect(TEX_VERTEX.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(TEX_VERTEX).position(0)private val vertexIndexBuffer = ByteBuffer.allocateDirect(VERTEX_INDEX.size * 2).order(ByteOrder.nativeOrder()).asShortBuffer().put(VERTEX_INDEX).position(0)private var textureId = 0var glWidth: Float = 0fprivate setvar glHeight: Float = 0fprivate setinit {textureId = GLUtil.loadTexture(bitmap)val cx = 0fval cy = 0fval xOffset = 0.0005f * bitmap.widthval yOffset = 0.0005f * bitmap.heightglWidth = xOffset * 2fglHeight = yOffset * 2fpositionVertex = floatArrayOf(cx, cy, 0f,xOffset, yOffset, 0f,-xOffset, yOffset, 0f,-xOffset, -yOffset, 0f,xOffset, -yOffset, 0f)vertexBuffer.position(0)vertexBuffer.put(positionVertex)vertexBuffer.position(0)}fun draw(vbo: IntArray) {glBindBuffer(GL_ARRAY_BUFFER, vbo[0])glBufferData(GL_ARRAY_BUFFER, vertexBuffer.capacity() * 4, vertexBuffer, GL_STATIC_DRAW)glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0)glEnableVertexAttribArray(0)glBindBuffer(GL_ARRAY_BUFFER, vbo[1])glBufferData(GL_ARRAY_BUFFER, texVertexBuffer.capacity() * 4, texVertexBuffer, GL_STATIC_DRAW)glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0)glEnableVertexAttribArray(1)//激活纹理glActiveTexture(GL_TEXTURE0)//绑定纹理glBindTexture(GL_TEXTURE_2D, textureId)// 绘制glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[2])glBufferData(GL_ELEMENT_ARRAY_BUFFER, VERTEX_INDEX.size * 2, vertexIndexBuffer, GL_STATIC_DRAW)glDrawElements(GL_TRIANGLES, VERTEX_INDEX.size, GL_UNSIGNED_SHORT, 0)}companion object {private const val TAG = "GLChar"/*** 绘制顺序索引*/private val VERTEX_INDEX = shortArrayOf(0, 1, 2,  //V0,V1,V2 三个顶点组成一个三角形0, 2, 3,  //V0,V2,V3 三个顶点组成一个三角形0, 3, 4,  //V0,V3,V4 三个顶点组成一个三角形0, 4, 1   //V0,V4,V1 三个顶点组成一个三角形)/*** 纹理坐标* (s,t)*/private val TEX_VERTEX = floatArrayOf(0.5f, 0.5f, //纹理坐标V01f, 0f,     //纹理坐标V10f, 0f,     //纹理坐标V20f, 1.0f,   //纹理坐标V31f, 1.0f    //纹理坐标V4)}
}

 GLText.tk

class GLText(text: String, glChars: Map<Char, GLChar>) {private val glCharList = mutableListOf<GLChar>()init {for (c in text) glChars[c]?.let(glCharList::add)}fun draw(vbo: IntArray, offsetBlock: (Float, Float)->Unit) {val textWidth = glCharList.sumOf { it.glWidth.toDouble() }.toFloat()var xOffset = -textWidth / 2ffor (glChar in glCharList) {offsetBlock(xOffset, 0f)glChar.draw(vbo)xOffset += glChar.glWidth}}
}

RendererText.kt

import android.content.Context
import android.opengl.GLES30.*
import android.opengl.GLSurfaceView
import android.opengl.Matrix
import java.nio.ByteBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10class RendererText(private val context: Context) : GLSurfaceView.Renderer, IShaderProvider {private val numVAOs = 1private val numVBOs = 3private val vao = IntArray(numVAOs)private val vbo = IntArray(numVBOs)private var cameraX = 0fprivate var cameraY = 0fprivate var cameraZ = 2.5fprivate var renderingProgram = 0private var mvLoc = 0private var projLoc = 0private val pMat = FloatArray(16)private val vMat = FloatArray(16)private val mMat = FloatArray(16)private val mvMat = FloatArray(16)private val glChars = mutableMapOf<Char, GLChar>()private var glText = GLText("", glChars)private fun loadGLChars() {val ft = JFreeType()val faceBuffer = context.assets.open("fonts/SourceCodePro-Regular.ttf").use {ByteBuffer.allocateDirect(it.available()).put(it.readBytes()).apply { position(0) }}ft.init(faceBuffer)val chars = mutableListOf<Char>()fun putChar(char: Char) {chars.add(char)}fun putChars(range: IntRange) {for (charcode in range) putChar(charcode.toChar())}putChars('A'.code..'Z'.code)putChars('a'.code..'z'.code)putChars('0'.code..'9'.code)putChar('!')val ftBitmaps = chars.map {val ftBitmap = FTBitmap()ft.charBitmap(ftBitmap, it)ftBitmap}var maxAscent = 0var maxDescent = 0for (ftBmp in ftBitmaps) {if (ftBmp.bitmapTop > maxAscent) maxAscent = ftBmp.bitmapTopif (ftBmp.rows - ftBmp.bitmapTop > maxDescent) maxDescent = ftBmp.rows - ftBmp.bitmapTop}for (i in chars.indices) {ftBitmaps[i].toBitmap(maxAscent, maxDescent)?.let { bitmap ->glChars[chars[i]] = GLChar(bitmap)}}ft.close()glText = GLText("HelloWorld!", glChars)}override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {renderingProgram = GLUtil.createShaderProgram(vertexShaderSource(), fragmentShaderSource())glUseProgram(renderingProgram)mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix")projLoc = glGetUniformLocation(renderingProgram, "proj_matrix")glGenVertexArrays(1, vao, 0)glBindVertexArray(vao[0])glGenBuffers(numVBOs, vbo, 0)loadGLChars()}override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {glViewport(0, 0, width, height)val aspect = width.toFloat() / height.toFloat()Matrix.perspectiveM(pMat, 0, Math.toDegrees(1.0472).toFloat(), aspect, 0.1f, 1000f)}override fun onDrawFrame(p0: GL10?) {glClearColor(0f, 0f, 0f, 1f)glClear(GL_COLOR_BUFFER_BIT)//下面两行代码,防止图片的透明部分被显示成黑色glEnable(GL_BLEND)glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)Matrix.setIdentityM(vMat, 0)Matrix.translateM(vMat, 0, -cameraX, -cameraY, -cameraZ)Matrix.setIdentityM(mMat, 0)Matrix.multiplyMM(mvMat, 0, vMat, 0, mMat, 0)glUniformMatrix4fv(mvLoc, 1, false, mvMat, 0)glUniformMatrix4fv(projLoc, 1, false, pMat, 0)glText.draw(vbo) { xOffset, yOffset ->Matrix.setIdentityM(mMat, 0)Matrix.translateM(mMat, 0, xOffset, yOffset, 0f)Matrix.multiplyMM(mvMat, 0, vMat, 0, mMat, 0)glUniformMatrix4fv(mvLoc, 1, false, mvMat, 0)}}override fun vertexShaderSource(): String {return """#version 300 eslayout (location = 0) in vec3 position;layout (location = 1) in vec2 tex_coord;out vec2 tc;uniform mat4 mv_matrix;uniform mat4 proj_matrix;uniform sampler2D s;void main(void){gl_Position = proj_matrix * mv_matrix * vec4(position, 1.0);tc = tex_coord;}""".trimIndent()}override fun fragmentShaderSource(): String {return """#version 300 esprecision mediump float;in vec2 tc;out vec4 color;uniform sampler2D s;void main(void){color = texture(s,tc);}""".trimIndent()}
}

效果图

3、总结

字符转位图,照着FreeType的文档很容易就实现了,其中关于字符水平对齐稍微花了点时间,后结合文档Managing Glyphs以及观察打印的数据,确定 bitmap_left 就是 bearingX,bitmap_top 是 bearingY,这样很容易把水平方向的字符按照 baseline 对齐。

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

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

相关文章

[论文笔记] Open-Sora 1、sora复现方案概览

GitHub - hpcaitech/Open-Sora: Unofficial implementation of OpenAIs Sora Open-Sora已涵盖: 提供完整的Sora复现架构方案,包含从数据处理到训练推理全流程。 支持动态分辨率,训练时可直接训练任意分辨率的视频,无需进行缩放。 支持多种模型结构。由于Sora实际模型结构未…

部署LVS负载均衡架构

目录 一、ipvsadm 工具 二、NAT模式下部署LVS负载均衡 1、部署NFS共享存储服务器 1.1 安装NFS软件 1.2 新建共享目录和站点文件 1.3 设置共享策略 2、部署节点服务器1 2.1 安装并启动nginx软件 2.2 挂载共享目录到网页站点目录 2.3 修改网关 3、部署节点服务器2 3.…

Python爬虫入门教程

一、爬虫的概念 爬虫是模拟浏览器发送请求&#xff0c;获取响应 二、爬虫的流程 url—>发送请求&#xff0c;获取响应—>提取数据—》保存发送请求&#xff0c;获取响应—>提取url 爬虫要根据当前url地址对应的响应为准 &#xff0c;当前url地址的elements的内容和…

在ABAP中创建一个简单的守护进程

原文地址&#xff1a;Create a simple Daemon in ABAP 目录 一、ABAP语言中的守护进程是什么&#xff1f;二、ABAP 守护进程框架 (ADF)三、ABAP 守护进程类四、创建一个简单的ABAP守护进程步骤1&#xff1a;创建一个新的ABAP Daemon类步骤2&#xff1a;实现ON_ACCEPT方法第三步…

「滚雪球学Java」:GUI编程(章节汇总)

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

Kosmos-1: 通用接口架构下的多模态大语言模型

Kosmos-1: 通用接口架构下的多模态大语言模型 FesianXu 20230513 at Baidu Search Team 前言 在大规模语言模型&#xff08;Large Language Model, LLM&#xff09;看似要带来新一番人工智能变革浪潮之际&#xff0c;越来越多尝试以LLM作为通用接口去融入各种任务的工作&#…

【vue】ant-design弹出框无法关闭和runtimecore提示isFucntion is not function的问题修复

【vue】ant-design弹出框无法关闭和runtimecore提示isFucntion is not function的问题修复&#xff0c;初步分析是vue发布3.4版本以后引起的兼容性问题。 问题截图&#xff1a; 1.isFucntion is not function&#xff0c;是由于vue升级后众多插件版本不匹配造成的问题 2.弹框…

计算机中msvcp140.dll,丢失怎么修复与解决

一、msvcp140.dll20个软件环境 msvcp140.dll文件是许多软件运行环境的组成部分&#xff0c;通常与Microsoft Visual C Redistributable关联。以下是可能使用该文件的软件环境&#xff1a; 微软办公软件&#xff1a;如Microsoft Office套件&#xff0c;包括Word、Excel、Power…

Python给图片加水印

受到“手动给证件加文字太麻烦”的感触&#xff0c;想用Python来实现给图片加水印&#xff0c;这不方便多了。 这里使用PIL模块&#xff1a; from PIL import Image from PIL import ImageFont from PIL import ImageDrawimg_t Image.open(cat.jpg) img_size_t img_t.size…

OJ:循环队列

622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09; 思路 思路&#xff1a;首先循环队列的意思是&#xff1a;空间固定&#xff0c;就是提前开辟好&#xff0c;满了就不能插入了&#xff0c;但是删除数据后仍有空间&#xff0c;删除循环队列里面的数据后&#xff0c;保…

Apache ECharts数据可视化技术

介绍 官方地址:Apache ECharts 快速入门案例echarts.init //初始化方法 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>ECharts</title><!-- 引入刚刚下载的 ECharts 文件 --><script src"echart…

基于JavaWEB SpringBoot婚纱影楼摄影预约网站设计和实现

基于JavaWEB SSM SpringBoot婚纱影楼摄影预约网站设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言…

《The Art of InnoDB》第二部分|第5章:深入结构-内存结构-缓冲区Buffer Pool

5.1 缓冲区Buffer Pool 目录 5.1 缓冲区Buffer Pool 5.1.1 Buffer Pool 结构 5.1.2 LRU List 5.1.3 Page Hash 5.1.4 Flush List 5.1.5 Change Buffer 5.1.6 小结 5.1.1 Buffer Pool 结构 Buffer Pool(缓冲池)在InnoDB存储引擎中是一个包罗万象的概念。它主要用于缓…

Java Swing游戏开发学习8

内容来自RyiSnow视频讲解 上一节提到的bug&#xff0c;不知道有没有人发现&#xff1f; 在播放音乐和音效的时候使用的是同一个clip对象&#xff0c;播放背景音乐在前&#xff0c;后续播放音效&#xff0c;clip对象就被覆盖了&#xff0c;因此导致调用停止播放背景音乐的时候&a…

计算机组成原理之机器:总线

计算机组成原理之机器 笔记来源&#xff1a;哈尔滨工业大学计算机组成原理&#xff08;哈工大刘宏伟&#xff09; Chapter2&#xff1a;总线 2.1 总线的基本概念 1.为什么需要总线&#xff1f;有几百个部件需要连接进行信息传输 2.什么是总线&#xff1f;总线是连接各个部件…

七、链表问题(上)

160、相交链表&#xff08;简单&#xff09; 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个…

如何选择阿里云服务器配置?(CPU/内存/带宽/磁盘)

阿里云服务器配置怎么选择&#xff1f;CPU内存、公网带宽和系统盘怎么选择&#xff1f;个人开发者或中小企业选择轻量应用服务器、ECS经济型e实例&#xff0c;企业用户选择ECS通用算力型u1云服务器、ECS计算型c7、通用型g7云服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com整…

对象得定义与使用(动力节点老杜)

对象思想 1.什么是面向过程&#xff0c;什么是面向对象&#xff1f; 换而言之&#xff0c;面向对象思想实际就是将整体分成一个个独立的单元&#xff0c;每个单元都有自己得任务和属性&#xff0c;所有单元结合在一起完成一个整体。如果某个单元出现了问题还可以及时处理&…

比肩Gen-2,全新开源文生视频模型

著名开源平台Stability.ai在官网宣布&#xff0c;推出全新文生视频的扩散模型Stable Video Diffusion&#xff0c;已开源了该项目并公布了论文。 据悉&#xff0c;用户通过文本或图像就能生成高精准&#xff0c;14帧和25帧的短视频。目前&#xff0c;Stable Video Diffusion处…

回溯算法02-组合总合III(Java)

2.组合总合III 题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3,…