rk 关机充电的显示基于 minui 显示框架 , 它具有简单的图形绘制, 以及解析 png 图片并将其简单的显示出来的能力. 它支持三种显示框架 fbdev 、drm 和 adf . 由于需要解析 png 图片因此也需要使用到 libpng 库 .
充电 logo 显示自身包括一个服务 charger
. 用于监测按键是否按下 、充电图片的解析显示 , 以及按键状态的处理.
一、charger 线程
同样的根据 bp 文件跟踪调用链.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 cc_binary { name: "charger" , defaults: ["charger_defaults" ], recovery_available: true , srcs: [ "charger.cpp" , "charger_utils.cpp" , ], target: { recovery: { cflags: [ "-DCHARGER_FORCE_NO_UI=1" , ], exclude_shared_libs: [ "libpng" , ], exclude_static_libs: [ "libhealthd_draw" , "libhealthd_charger" , "libminui" , "libsuspend" , ], } } }
调用链接如下
1 2 healthd_charger_main (argc, argv) --> harger.StartLoop ();
最终调用 StartLoop 接口, 它是哪来的呢? 查看 charge 类的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Charger : public ::android::hardware::health::V2_1::implementation::HalHealthLoop { public : using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo; using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo; Charger (const sp<android::hardware::health::V2_1::IHealth>& service); ~Charger (); protected : void Heartbeat () override ; int PrepareToWait () override ; void Init (struct healthd_config* config) override ; void OnHealthInfoChanged (const HealthInfo_2_1& health_info) override ; ...... animation batt_anim_; GRSurface* surf_unknown_ = nullptr ; int boot_min_cap_ = 0 ; HealthInfo_1_0 health_info_ = {}; std::unique_ptr<HealthdDraw> healthd_draw_; std::vector<animation::frame> owned_frames_; }; }
通过类可以知道 charge 继承了 HalHealthLoop 并在此基础上重写了 Heartbeat()
、PrepareToWait()
、OnHealthInfoChanged()
、Init()
方法.
HalHealthLoop
类由 hall 层提供对应的路径 hardware/interfaces/health/2.0/utils/libhealthservice/*
. 它提供的 StartLoop 方法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int HealthLoop::StartLoop () { int ret; klog_set_level (KLOG_LEVEL); ret = InitInternal (); if (ret) { KLOG_ERROR (LOG_TAG, "Initialization failed, exiting\n" ); return 2 ; } MainLoop (); KLOG_ERROR (LOG_TAG, "Main loop terminated, exiting\n" ); return 3 ; }
该方法调用了 MainLoop()
方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void HealthLoop::MainLoop (void ) { int nevents = 0 ; while (1 ) { reject_event_register_ = true ; size_t eventct = event_handlers_.size (); struct epoll_event events[eventct]; int timeout = awake_poll_interval_; int mode_timeout; if (!nevents) PeriodicChores (); Heartbeat (); mode_timeout = PrepareToWait (); if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout; nevents = epoll_wait (epollfd_, events, eventct, timeout); if (nevents == -1 ) { if (errno == EINTR) continue ; KLOG_ERROR (LOG_TAG, "healthd_mainloop: epoll_wait failed\n" ); break ; } for (int n = 0 ; n < nevents; ++n) { if (events[n].data.ptr) { auto * event_handler = reinterpret_cast <EventHandler*>(events[n].data.ptr); event_handler->func (event_handler->object, events[n].events); } } } return ; }
MainLoop()
就是一个循环, 在循环中有两件事情.
通过 Heartbeat()
根据按键状态亮屏 , 根据充电状态判断是否亮屏 , 更新充电 logo 显示 .
监测 event 状态, 发生事件之后调用 event_handler->func
更新按键状态. 它通过 int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup)
接口注册
其中关键的就是 charger 重写的 Heartbeat 方法.
1 2 3 4 5 6 7 8 9 10 void Charger::Heartbeat () { int64_t now = curr_time_ms (); LOGV ("[%" PRId64 "] Heartbeat\n" , now); HandleInputState (now); HandlePowerSupplyState (now); UpdateScreenState (now); }
这个实现逻辑是有问题的, 不支持按键灭屏, 拔出充电器之后, 会先亮屏显示一次充电 logo 再灭屏关机.
1、根据按键状态亮屏.
长按则使用 reboot 命令重启机器, 重启时间等于 POWER_ON_KEY_TIME , 短按亮屏 , 如果是 key->pending 也亮屏 .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 void Charger::HandleInputState (int64_t now) { ProcessKey (KEY_POWER, now); if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1 ; } void Charger::ProcessKey (int code, int64_t now) { key_state* key = &keys_[code]; LOGV ("[%" PRId64 "] key[%d] ProcessKey\n" , now, code); if (code == KEY_POWER) { if (key->down) { int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; if (now >= reboot_timeout) { if (property_get_bool ("ro.enable_boot_charger_mode" , false )) { LOGE ("[%" PRId64 "] booting from charger mode\n" , now); property_set ("sys.boot_from_charger_mode" , "1" ); } else { if (batt_anim_.cur_level >= boot_min_cap_) { LOGE ("[%" PRId64 "] rebooting\n" , now); reboot (RB_AUTOBOOT); } else { LOGV ("[%" PRId64 "] ignore power-button press, battery level " "less than minimum\n" , now); } } } else { SetNextKeyCheck (key, POWER_ON_KEY_TIME); kick_animation (&batt_anim_); request_suspend (false ); } } else { if (key->pending) { kick_animation (&batt_anim_); request_suspend (false ); } } } key->pending = false ; }
2、充电状态判断是否亮屏
它实现俩个功能, 没插充电器超时关机、第一次插入充电器亮屏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void Charger::HandlePowerSupplyState (int64_t now) { int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME; if (!have_battery_state_) return ; if (!charger_online ()) { request_suspend (false ); if (next_pwr_check_ == -1 ) { timer_shutdown = property_get_int32 (UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME); next_screen_transition_ = now - 1 ; reset_animation (&batt_anim_); kick_animation (&batt_anim_); next_pwr_check_ = now + timer_shutdown; LOGE ("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n" , now, (int64_t )timer_shutdown, next_pwr_check_); } else if (now >= next_pwr_check_) { LOGE ("[%" PRId64 "] shutting down\n" , now); reboot (RB_POWER_OFF); } else { } } else { if (next_pwr_check_ != -1 ) { request_suspend (false ); next_screen_transition_ = now - 1 ; reset_animation (&batt_anim_); kick_animation (&batt_anim_); LOGE ("[%" PRId64 "] device plugged in: shutdown cancelled\n" , now); } next_pwr_check_ = -1 ; } }
3、更新充电 logo 显示
3.1 UpdateScreenState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void Charger::UpdateScreenState (int64_t now) { ...... healthd_draw_->redraw_screen (&batt_anim_, surf_unknown_); .... } void HealthdDraw::redraw_screen (const animation* batt_anim, GRSurface* surf_unknown) { if (!graphics_available) return ; clear_screen (); if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 || batt_anim->num_frames == 0 ) draw_unknown (surf_unknown); else draw_battery (batt_anim); gr_flip (); }
3.2 draw_battery
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void HealthdDraw::draw_battery (const animation* anim) { int y_start = 0 ; printf ("drawing frame #%d\n" , anim->cur_frame); if (!graphics_available) return ; const animation::frame& frame = anim->frames[anim->cur_frame]; if (anim->num_frames != 0 ) { y_start = draw_surface_centered (frame.surface); printf ("drawing frame #%d min_cap=%d time=%d\n" , anim->cur_frame, frame.min_level, frame.disp_time); } draw_clock (anim); draw_percent (anim, y_start); }
3.3 draw_surface_centered
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int HealthdDraw::draw_surface_centered (GRSurface* surface) { if (!graphics_available) return 0 ; int w = gr_get_width (surface); int h = gr_get_height (surface); int x = (screen_width_ - w) / 2 + kSplitOffset; int y = (screen_height_ - h) / 2 ; printf ("drawing surface %dx%d+%d+%d\n" , w, h, x, y); gr_blit (surface, 0 , 0 , w, h, x, y); if (kSplitScreen) { x += screen_width_ - 2 * kSplitOffset; printf ("drawing surface %dx%d+%d+%d\n" , w, h, x, y); gr_blit (surface, 0 , 0 , w, h, x, y); } return y + h; }
3.4 draw_percent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void HealthdDraw::draw_percent (const animation* anim, int y_start) { if (!graphics_available) return ; int cur_level = anim->cur_level; if (anim->cur_status == BATTERY_STATUS_FULL) { cur_level = 100 ; } if (cur_level < 0 ) return ; const animation::text_field& field = anim->text_percent; if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0 ) { return ; } std::string str = base::StringPrintf ("%d%%" , cur_level); int x, y; determine_xy (field, str.size (), &x, &y); gr_color (field.color_r, field.color_g, field.color_b, field.color_a); gr_text (field.font, x, y_start, str.c_str (), false ); }
二、客制化充电图片
默认的原图非常丑陋, 因此对充电图片进行客制化. 充电图片的位置在 system\core\healthd\images
, 编译后会被打包到 ```res/images/charger`` 目录. 根据前面的显示流程可以知道关键在于 batt_anim_ 它是一个 animation 类型的对象, 保存在charger 对象里面.
1 2 3 4 5 6 class Charger : public ::android::hardware::health::V2_1::implementation::HalHealthLoop { ...... private : animation batt_anim_; ...... };
2、 修改 animation 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 -- a/healthd/animation.h @@ -23,6 +23,8 @@ class GRSurface; struct GRFont; +#define CHARGER_USER_ANIMATION + namespace android { #define CENTER_VAL INT_MAX @@ -48,11 +50,20 @@ struct animation { GRFont* font; }; + #define USER_IMAGE_NUM 6 + // When libminui loads PNG images: // - When treating paths as relative paths, it adds ".png" suffix. // - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix // is added here. void set_resource_root(const std::string& root) { + + for (int i=0; i < USER_IMAGE_NUM; i++){ + if (!usr_animation_file[i].empty()) { + usr_animation_file[i] = root + usr_animation_file[i] + ".png"; + } + } + if (!animation_file.empty()) { animation_file = root + animation_file + ".png"; } @@ -67,6 +78,7 @@ struct animation { } } + std::string usr_animation_file[USER_IMAGE_NUM]; std::string animation_file; std::string fail_file;
2、修改 InitAnimation 方法
在 InitAnimation 方法中获取 png 图片的路径等信息, 默认的图片是 battery_scale.png , 一张图片里面有很多图片合成的, 通过解析一张图片就能得到 6 帧的信息. 我客制化用的图片是分开的6帧, 因此需要在这里逐一添加对应的信息. 代码路径 system/core/healthd/healthd_mode_charger.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 -- a/healthd/healthd_mode_charger.cpp @@ -58,6 +58,10 @@ #include <health2impl/Health.h> #include <healthd/healthd.h> +#ifndef CHARGER_USER_ANIMATION +#define CHARGER_USER_ANIMATION +#endif + using namespace android; using android::hardware::Return; using android::hardware::health::GetHealthServiceOrDefault; @@ -634,7 +638,17 @@ void Charger::InitAnimation() { if (!parse_success) { LOGW("Could not parse animation description. Using default animation.\n"); batt_anim_ = BASE_ANIMATION; + +#ifdef CHARGER_USER_ANIMATION + batt_anim_.usr_animation_file[0].assign("charger/battery_0"); + batt_anim_.usr_animation_file[1].assign("charger/battery_1"); + batt_anim_.usr_animation_file[2].assign("charger/battery_2"); + batt_anim_.usr_animation_file[3].assign("charger/battery_3"); + batt_anim_.usr_animation_file[4].assign("charger/battery_4"); + batt_anim_.usr_animation_file[5].assign("charger/battery_5"); +#else batt_anim_.animation_file.assign("charger/battery_scale"); +#endif InitDefaultAnimationFrames(); batt_anim_.frames = owned_frames_.data(); batt_anim_.num_frames = owned_frames_.size();
3、修改 Init 方法
在 minui 中每一个图像都由一个 GRSurface 描述. init 方法中调用 res_create_display_surface 则用来解析前面配置的 usr_animation_file[i] 对应的目录下的 png 图片并输出 GRSurface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 -- a/healthd/healthd_mode_charger.cpp @@ -691,6 +705,19 @@ void Charger::Init(struct healthd_config* config) { } } +#ifdef CHARGER_USER_ANIMATION + GRSurface* scale_frames[USER_IMAGE_NUM]; + for(i = 0; i < USER_IMAGE_NUM; i++){ + ret = res_create_display_surface(batt_anim_.usr_animation_file[i].c_str(), &scale_frames[i]); + if (ret < 0) { + LOGE("Cannot load custom %s image. Reverting to built in.\n",batt_anim_.usr_animation_file[i].c_str()); + }else{ + batt_anim_.frames[i].surface = scale_frames[i]; + LOGE("file is:[%s],batt_anim_.frames[%d].surface = charger->surf_unknown;\n", + batt_anim_.usr_animation_file[i].c_str(),i); + } + } +#else GRSurface** scale_frames; int scale_count; int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text @@ -711,6 +738,8 @@ void Charger::Init(struct healthd_config* config) { batt_anim_.frames[i].surface = scale_frames[i]; } } +#endif
需要注意的是默认的 minui 只支解析持单通道的 png 因此需要修改. 为我使用的平台是 rk3566, drm 驱动是支持 4 通道的图片显示, 4 通道的图片就是 ARGB 图片. 单通道就是 8 位描述 rgb 信息. 默认的图片就是单通道的灰度图, 直接注释掉就行了.
1 2 3 4 5 6 7 8 9 10 11 12 -- a/minui/resources.cpp @@ -128,7 +128,7 @@ PngHandler::PngHandler(const std::string& name) { } else { fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", bit_depth_, channels_, color_type_); - error_code_ = -7; + //error_code_ = -7; } }
三、minui 框架
安卓提供的一个进行简单图像显示框架
1、函数接口
1.1 gr_init
获取系统的显示像素format
由device/rockchip/common/BoardConfig.mk
中的 TARGET_RECOVERY_PIXEL_FORMAT1
配置. (ABGR_8888/RGBX_8888/ARGB_8888/ARGB_8888)
调用 adf 或 drm 或 fbdev (谁成功用谁), 提供的接口初始化显示接口, 并返回 gr_draw 用来描述显存信息
获取由device/rockchip/common/device.mk
中的TARGET_RECOVERY_OVERSCAN_PERCENT
配置的overscan_percent
初始化硬件 pip
获取由device/rockchip/common/BoardConfig.mk
中的TARGET_RECOVERY_DEFAULT_ROTATION
配置的系统的方向rotation_str
1.2 gr_flip
回调 gr_flip 初始化硬件 pip 并显示 gr_draw.data() 显存中的图片.
1.3 简单通用接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static void incr_x (uint32_t ** p, int row_pixels) ;static void incr_y (uint32_t ** p, int row_pixels) ;static bool outside (int x, int y) ;static uint32_t * PixelAt (GRSurface* surface, int x, int y, int row_pixels) ;unsigned int gr_get_width (const GRSurface* surface) ;unsigned int gr_get_height (const GRSurface* surface) ;int gr_measure (const GRFont* font, const char * s) ;
1.4 gr_color
设置背景画布, 如果 pixel_format 是 ARGB 或者 BGRA 则将 gr_current(背景画布) 统一为 ARGB , 否则转换为 ABGR
1 2 3 4 void gr_color (unsigned char r, unsigned char g, unsigned char b, unsigned char a) ;
1.5 gr_blit
将起点为 (sx,sy) 宽高为 (w,h) 的 source 描述原图复制到显存 (dx,dy) 处
1 2 3 4 5 void gr_blit (const GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) ;
1.6 gr_clear
清屏, 即将屏幕清理为 gr_current 描述的颜色, 如果是灰度图片则调用 memset 提高效率, 因此在 set_clear 前要 set_color 设置画布的像素颜色 gr_current
1.7 字符串显示接口
1 2 3 int gr_init_font (const char * name, GRSurface** pSurface)
初始化 font 对应的 png, 其实就是一张包含了 assic 码表的图片, 充电百分比显示的数字就是直接解析图片获取的. 但是默认情况只支持单通的黑白图片. 如下图所示, 内容的顺序就是 assic 的顺序
1 static inline uint32_t pixel_blend (uint8_t alpha, uint32_t pix) ;
图像合成, alpha 表示灰度图像的亮度级别,范围从 0 到 255,其中 0 表示完全黑(最暗),255 表示完全白(最亮),这里的 alpha 被用作亮度级别,而不是真正的透明度。根据 alpha 的亮度级别以及 pix 和 gr_current 的颜色值来生成一个混合后的 RGB 像素.
1 static void TextBlend (const uint8_t * src_p, int src_row_bytes, uint32_t * dst_p, int dst_row_pixels, int width, int height) ;
将 src_p 指向的灰度图片, 合成到 dst_p 指向的源图, 图片的宽为 width, 高为 height.
1 void gr_text (const GRFont* font, int x, int y, const char * s, bool bold) ;
显示 s 中的字符, 字符的来源为 font->texture->data() 指向的图片, blod 表示是否加粗. texture 就是 gr_init_font 返回的 GRSurface 对象.
四、支持 4通道百分比图片显示
修改 minui 支持 4 通道, 百分比图片显示, 完整 patch
1、修改 graphics.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 -- a/minui/graphics.cpp @@ -139,6 +139,24 @@ static uint32_t* PixelAt(GRSurface* surface, int x, int y, int row_pixels) { return nullptr; } +static void TextBlend_4channel(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, + int width, int height) { + + for (int j = 0; j < height; ++j) { + const uint32_t* sx = reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(src_p));; + uint32_t* px = dst_p; + + for (int i = 0; i < width; ++i, incr_x(&px, dst_row_pixels)) { + *px = *sx ++; + } + + src_p += src_row_bytes; + incr_y(&dst_p, dst_row_pixels); + } +} + static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels, int width, int height) { uint8_t alpha_current = static_cast<uint8_t>((alpha_mask & gr_current) >> 24); @@ -158,10 +176,12 @@ static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) { if (!font || !font->texture || (gr_current & alpha_mask) == 0) return; - if (font->texture->pixel_bytes != 1) { - printf("gr_text: font has wrong format\n"); - return; - } + // if (font->texture->pixel_bytes != 1) { + // printf("gr_text: font has wrong format\n"); + // return; + // } bold = bold && (font->texture->height != font->char_height); @@ -181,10 +201,17 @@ void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) { (bold ? font->char_height * font->texture->row_bytes : 0); uint32_t* dst_p = PixelAt(gr_draw, x, y, row_pixels); - TextBlend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, + if (font->texture->pixel_bytes != 1) { + TextBlend_4channel(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width / 4, font->char_height); - - x += font->char_width; + x += font->char_width / 4; + }else{ + TextBlend(src_p, font->texture->row_bytes, dst_p, row_pixels, font->char_width, + font->char_height); + x += font->char_width; + } } } @@ -329,7 +356,15 @@ int gr_init_font(const char* name, GRFont** dest) { // The font image should be a 96x2 array of character images. The // columns are the printable ASCII characters 0x20 - 0x7f. The // top row is regular text; the bottom row is bold. - font->char_width = font->texture->width / 96; + + if(font->texture->pixel_bytes != 1){ + font->char_width = font->texture->width * 4 / 96; + }else{ + font->char_width = font->texture->width / 96; + } + font->char_height = font->texture->height / 2; *dest = font;
2、修改 resources.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 -- a/minui/resources.cpp @@ -153,6 +153,7 @@ static void TransformRgbToDraw(const uint8_t* input_row, uint8_t* output_row, in int width) { const uint8_t* ip = input_row; uint8_t* op = output_row; + uint8_t a, r, g, b; switch (channels) { case 1: @@ -178,7 +179,18 @@ static void TransformRgbToDraw(const uint8_t* input_row, uint8_t* output_row, in case 4: // copy RGBA to RGBX - memcpy(output_row, input_row, width * 4); + for (int x = 0; x < width * 4; ++x) { + r = *ip++; + g = *ip++; + b = *ip++; + a = *ip++; + *op++ = b; + *op++ = g; + *op++ = r; + *op++ = a; + } + +// memcpy(output_row, input_row, width * 4); break; } } @@ -299,20 +311,30 @@ exit: int res_create_alpha_surface(const char* name, GRSurface** pSurface) { *pSurface = nullptr; + std::unique_ptr<GRSurface> surface; PngHandler png_handler(name); if (!png_handler) return png_handler.error_code(); - if (png_handler.channels() != 1) { - return -7; - } + // if (png_handler.channels() != 1) { + // return -7; + // } png_structp png_ptr = png_handler.png_ptr(); png_uint_32 width = png_handler.width(); png_uint_32 height = png_handler.height(); - auto surface = GRSurface::Create(width, height, width, 1); - if (!surface) { + if (png_handler.channels() != 1) { + surface = GRSurface::Create(width, height, width * 4, 4); + }else{ + surface = GRSurface::Create(width, height, width, 1); + } + + if (!surface){ return -8; } @@ -321,10 +343,22 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { png_set_bgr(png_ptr); } - for (png_uint_32 y = 0; y < height; ++y) { - uint8_t* p_row = surface->data() + y * surface->row_bytes; - png_read_row(png_ptr, p_row, nullptr); + + if (png_handler.channels() != 1) { + for (png_uint_32 y = 0; y < height; ++y) { + std::vector<uint8_t> p_row(width * 4); + png_read_row(png_ptr, p_row.data(), nullptr); + TransformRgbToDraw(p_row.data(), surface->data() + y * surface->row_bytes, + png_handler.channels(), width); + } + }else{ + for (png_uint_32 y = 0; y < height; ++y) { + uint8_t* p_row = surface->data() + y * surface->row_bytes; + png_read_row(png_ptr, p_row, nullptr); + } } *pSurface = surface.release();
3. 修改 healthd_draw.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 -- a/healthd/healthd_draw.cpp @@ -135,9 +135,17 @@ int HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) { void HealthdDraw::determine_xy(const animation::text_field& field, const int length, int* x, int* y) { + int str_len_px; *x = field.pos_x; - int str_len_px = length * field.font->char_width; + if(field.font->texture->pixel_bytes == 1){ + str_len_px = length * field.font->char_width; + }else{ + str_len_px = length * field.font->char_width / 4; + } + if (field.pos_x == CENTER_VAL) { *x = (screen_width_ - str_len_px) / 2; } else if (field.pos_x >= 0) {
第如果四张图片不显示, 做如下修改.
1 2 3 4 5 6 7 8 9 10 @@ -181,7 +181,7 @@ void Charger::InitDefaultAnimationFrames() { }, { .disp_time = 750, - .min_level = 80, + .min_level = 0, .max_level = 95, .surface = NULL, },