Android开机动画,framework修改Bootanimation绘制文字。
文章目录
- Android开机动画,framework修改Bootanimation动画绘制文字。
- opengl绘制源码分析
Android开机动画,framework修改Bootanimation动画绘制文字。
frameworks/base/cmds/bootanimation/bootanimation.cpp
绘制时间的一个方法

// We render 12 or 24 hour time.
void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) {static constexpr char TIME_FORMAT_12[] = "%l:%M";static constexpr char TIME_FORMAT_24[] = "%H:%M";static constexpr int TIME_LENGTH = 6;
获取系统时间time_t rawtime;time(&rawtime);struct tm* timeInfo = localtime(&rawtime);char timeBuff[TIME_LENGTH];//显示时间的字符串const char* timeFormat = mTimeFormat12Hour ? TIME_FORMAT_12 : TIME_FORMAT_24;size_t length = strftime(timeBuff, TIME_LENGTH, timeFormat, timeInfo);if (length != TIME_LENGTH - 1) {SLOGE("Couldn't format time; abandoning boot animation clock");mClockEnabled = false;return;}char* out = timeBuff[0] == ' ' ? &timeBuff[1] : &timeBuff[0];int x = xPos;int y = yPos;//绘制文本drawText(out, font, false, &x, &y);
}
绘制文本
void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {glEnable(GL_BLEND); // Allow us to draw on top of the animationglBindTexture(GL_TEXTURE_2D, font.texture.name);const int len = strlen(str);const int strWidth = font.char_width * len;if (*x == TEXT_CENTER_VALUE) {*x = (mWidth - strWidth) / 2;} else if (*x < 0) {*x = mWidth + *x - strWidth;}if (*y == TEXT_CENTER_VALUE) {*y = (mHeight - font.char_height) / 2;} else if (*y < 0) {*y = mHeight + *y - font.char_height;}int cropRect[4] = { 0, 0, font.char_width, -font.char_height };for (int i = 0; i < len; i++) {char c = str[i];if (c < FONT_BEGIN_CHAR || c > FONT_END_CHAR) {c = '?';}// Crop the texture to only the pixels in the current glyphconst int charPos = (c - FONT_BEGIN_CHAR); // Position in the list of valid charactersconst int row = charPos / FONT_NUM_COLS;const int col = charPos % FONT_NUM_COLS;cropRect[0] = col * font.char_width; // Left of columncropRect[1] = row * font.char_height * 2; // Top of row// Move down to bottom of regular (one char_heigh) or bold (two char_heigh) linecropRect[1] += bold ? 2 * font.char_height : font.char_height;glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);glDrawTexiOES(*x, *y, 0, font.char_width, font.char_height);*x += font.char_width;}glDisable(GL_BLEND); // Return to the animation's default behaviourglBindTexture(GL_TEXTURE_2D, 0);
}
初始化字体

声明一个成员变量Font。

来到android()的initFont()

这是一个文件名字符串
static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
opengl只是支持图片纹理,所以文件是一张图片
然后会把这个图片加载进来,设置宽高等等。

绘制

对这个图片进行裁剪

我们新增代码在这里TEXT_CENTER_VALUE居中显示, yc + mAndroid[0].h计算绘制的y坐标系
yc是原本Android动画的一个坐标系,但是我们不能覆盖他,所以要比他高,放到原生Android动画的上边+ mAndroid[0].h
drawClock(mClockFont, TEXT_CENTER_VALUE, yc + mAndroid[0].h);

bool BootAnimation::android()
{SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",elapsedRealtime());initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");mCallbacks->init({});// clear screenglShadeModel(GL_FLAT);//qfh addbool hasInitFont = false;if (initFont(&mClockFont, CLOCK_FONT_ASSET) == NO_ERROR) {hasInitFont = true;ALOGD("android init Font ok ,fontname = %u",mClockFont.texture.name);}//qfh addglDisable(GL_DITHER);glDisable(GL_SCISSOR_TEST);glClearColor(0,0,0,1);glClear(GL_COLOR_BUFFER_BIT);eglSwapBuffers(mDisplay, mSurface);glEnable(GL_TEXTURE_2D);glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);const GLint xc = (mWidth - mAndroid[0].w) / 2;const GLint yc = (mHeight - mAndroid[0].h) / 2;// const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);//qfh modifyconst Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h*2);glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),updateRect.height() * 2);//qfh modify// Blend stateglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);const nsecs_t startTime = systemTime();do {nsecs_t now = systemTime();double time = now - startTime;float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;GLint x = xc - offset;glDisable(GL_SCISSOR_TEST);glClear(GL_COLOR_BUFFER_BIT);glEnable(GL_SCISSOR_TEST);glDisable(GL_BLEND);glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);glEnable(GL_BLEND);glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);drawClock(mClockFont, TEXT_CENTER_VALUE, yc + mAndroid[0].h);EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);if (res == EGL_FALSE)break;// 12fps: don't animate too fast to preserve CPUconst nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);if (sleepTime > 0)usleep(sleepTime);checkExit();} while (!exitPending());glDeleteTextures(1, &mAndroid[0].name);glDeleteTextures(1, &mAndroid[1].name);//qfh addif (hasInitFont)glDeleteTextures(1, &mClockFont.texture.name);//qfh addreturn false;
}
opengl绘制源码分析
安卓原生的开机动画是一个渐变色,由白色到灰色的渐变,直到launcher启动完成。
主要是这两个图片起作用

initTexture就是初始化纹理的意思,这就是aosp原生的动画图片,这个Android字样是镂空的,由其他图片去填充它,

下面这是第二张图片,也加载了

一白一灰,原理就是扫光动画,它在最底层,,两张图片叠加就可以动画了,把这张图片从左往右一直反复移动,就可看到一白一灰的渐变动画了。
看下这个方法做了什么

打开一个文件转换成Bitmap
Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);if (asset == nullptr)return NO_INIT;SkBitmap bitmap;sk_sp<SkData> data = SkData::MakeWithoutCopy(asset->getBuffer(false),asset->getLength());sk_sp<SkImage> image = SkImage::MakeFromEncoded(data);image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);asset->close();delete asset;

这两个才是opengl部分。绑定图片纹理图案,
glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),updateRect.height() * 2);
裁剪区域,不完全绘制屏幕的全部区域,选择性裁剪绘制显示区域。
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
叠加融合,就是两张图片叠加在一起绘制显示,
do {nsecs_t now = systemTime();double time = now - startTime;float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;GLint x = xc - offset;glDisable(GL_SCISSOR_TEST);glClear(GL_COLOR_BUFFER_BIT);glEnable(GL_SCISSOR_TEST);glDisable(GL_BLEND);//绑定,绘制mAndroid[1]的图片glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);//开启融合glEnable(GL_BLEND);//绑定 mAndroid[0]的图片,glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);drawClock(mClockFont, TEXT_CENTER_VALUE, yc + mAndroid[0].h);//调用opengl的方法显示到屏幕上EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);if (res == EGL_FALSE)break;// 12fps: don't animate too fast to preserve CPUconst nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);if (sleepTime > 0)usleep(sleepTime);checkExit();} while (!exitPending());
do,while循环绘制核心,开机动画是不断变化的,所以肯定在这个循环里实现,不断绘制的过程,绘制是有帧率的,循环不会一直执行的,因为会有功耗,所以12fps为一次绘制,一秒绘制12张图片
相关文章:
Android开机动画,framework修改Bootanimation绘制文字。
文章目录 Android开机动画,framework修改Bootanimation动画绘制文字。opengl绘制源码分析 Android开机动画,framework修改Bootanimation动画绘制文字。 frameworks/base/cmds/bootanimation/bootanimation.cpp 绘制时间的一个方法 // We render 12 or …...
2024河南高考作文ChatGPT
阅读下面的材料,根据要求写作。(60分) 随着互联网的普及、人工智能的应用,越来越多的问题能很快得到答案。那么,我们的问题是否会越来越少? 以上材料引发了你怎样的联想和思考?请写一篇文章。 要…...
整理好了!2024年最常见 20 道分布式、微服务面试题(一)
一、什么是分布式系统? 分布式系统是由多个独立的计算机(通常称为节点)组成的系统,这些计算机通过网络连接在一起,协同工作以完成一个共同的任务或服务。以下是分布式系统的关键特点和概念: 网络依赖性&am…...
要想数据形成好的数据集,必须数据治理(目的之一是防止大模型产生灰色数据等),用于炼丹(训练数据私有化模型)的数据才是好数据
数据治理:必要性、实施方法及挑战 引言 在当今数字化时代,数据已经成为企业最重要的资产之一。随着数据量的爆炸性增长,如何有效地管理和利用数据成为企业面临的重大挑战。数据治理(Data Governance)作为一种系统化的…...
外部mysql导入
利用这个命令: mysql -u username -p database_name < file.sql 然后就这样。成功导入。...
Qwen-VL论文阅读
论文地址 其他同学的详细讲解 模型结构和参数大小 (1)LLM:Qwen-7B (2)Vision Encoder:ViT架构,初始化参数是 Openclip’s ViT-bigG。 在训练和推理过程中,输入的图像都被调整到…...
超详细的java Comparable,Comparator接口解析
前言 Hello大家好呀,在java中我们常常涉及到对象的比较,不同于基本数据类型,对于我们的自定义对象,需要我们自己去建立比较标准,例如我们自定义一个People类,这个类有name和age两个属性,那么问…...
Java使用GDAL来解析KMZ及KML实战
目录 前言 一、在GQIS中浏览数据 1、关于空间参考 2、属性表格 二、GDAL的相关驱动及解析实战 1、GDAL中的KMZ驱动 2、GDAL实际解析 三、数据解析成果 1、KML解析结果 2、KMZ文件入库 四、总结 前言 在前面的博客中讲过纯Java实现Google地图的KMZ和KML文件的解析&…...
【vuex小试牛刀】
了解vuex核心概念请移步 https://vuex.vuejs.org/zh/ # 一、初始vuex # 1.1 vuex是什么 就是把需要共享的变量全部存储在一个对象里面,然后将这个对象放在顶层组件中供其他组件使用 父子组件通信时,我们通常会采用 props emit 这种方式。但当通信双方不…...
React - 实现走马灯组件
一、实现效果 二、源码分析 import {useRef, useState} from "react";export const Carousel () > {const images [{id: 3, url: https://sslstage3.sephorastatic.cn/products/2/4/6/8/1/6/1_n_new03504_100x100.jpg}, {id: 1, url: https://sslstage2.sephor…...
【学习笔记】Windows GDI绘图(十三)动画播放ImageAnimator(可调速)
文章目录 前言定义方法CanAnimate 是否可动画显示Animate 动画显示多帧图像UpdateFramesStopAnimate终止动画Image.GetFrameCount 获取动画总帧数Image.GetPropertyItem(0x5100) 获取帧延迟 自定义GIF播放(可调速) 前言 在前一篇文章中用到ImageAnimator获取了GIF动画的一些属…...
fps游戏如何快速定位矩阵
fps游戏如何快速定位矩阵 矩阵特点: 1、第一行第一列值的范围在**-1 ---- 1**之间,如果开镜之后值会变大。 2、第一行第三列的值始终为 0。 3、第一行第四列 的值比较大 , >300或者**<-300**。 根据这三个特点,定位矩阵已经足够了…...
【机器学习基础】Python编程06:五个实用练习题的解析与总结
Python是一种广泛使用的高级编程语言,它在机器学习领域中的重要性主要体现在以下几个方面: 简洁易学:Python语法简洁清晰,易于学习,使得初学者能够快速上手机器学习项目。 丰富的库支持:Python拥有大量的机器学习库,如scikit-learn、TensorFlow、Keras和PyTorch等,这些…...
R可视化:生存分析森林图
在R语言中,使用forestplot包来绘制生存分析的森林图是一个专业且直观的方式来展示各种风险因素或治疗对生存结果的影响。森林图(Forest Plot)常用于展示多项研究的效应量和其可信区间,尤其在生存分析中,它可以清晰地显示不同变量或因素对生存时间的影响程度和统计显著性。…...
一个 python+tensorFlow训练1万张图片分类的简单直观例子( 回答由百度 AI 给出 )
问题:给定一个文件夹 train_images,里面有10000张30*30像素的灰度值图片,第1~第10000张图片的名称分别为 00001.png、 00002.png、... 09999.png、10000.png,train_images 下面还有一个 image_category_map.txt文件, 文件的内容…...
DBeaver无法连接Clickhouse,连接失败
DBeaver默认下载的是0.2.6版本的驱动,但是一直连接失败: 报错提示 解决办法 点击上图中的Open Driver Configuration点击库 - 重置为默认状态在弹出的窗口中修改驱动版本号为0.2.4或者其他版本(我没有试用过其他版本)࿰…...
python基础实例
下一个更大的数 定义一个Solution类,用于实现next_great方法 class Solution: def next_great(self, nums1, nums2): # 初始化一个空字典answer,用于存储答案 answer {} # 初始化一个空列表stack,用于存储待比较的数字 stack [] # 遍历nu…...
ADASIS V2 协议-1
ADAS V2协议-1 1 简介2 版本控制3 ADASIS v23.1 ADASIS v2 Horizon (地平线)3.2 ADASIS v2的构建3.3 ADASIS v2 Horizon Provider (ADAS V2地平线提供者)3.4 paths and offsets (路径和偏移量)3.5 Path Pro…...
人工智能安全风险分析及应对策略
文│中国移动通信集团有限公司信息安全管理与运行中心 张峰 江为强 邱勤 郭中元 王光涛 人工智能(AI)是引领新一轮科技革命和产业变革的关键技术。人工智能赋能网络安全的同时,也会带来前所未有的安全风险。本文在介绍人工智能技术赋能网络安…...
Python驱动下的AI革命:技术赋能与案例解析
在当今这个信息化、数据化的时代,人工智能(AI)已经成为推动社会发展的重要力量。而Python,作为一种简单易学、功能强大的编程语言,在AI领域的应用中发挥着至关重要的作用。本文将探讨Python在AI领域的应用、其背后的技…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
