Merge branch 'main' of https://github.com/InfiniTimeOrg/InfiniTime
This commit is contained in:
commit
2bffcf99f0
@ -11,6 +11,7 @@ RUN apt-get update -qq \
|
||||
make \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-pil \
|
||||
tar \
|
||||
unzip \
|
||||
wget \
|
||||
|
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -31,6 +31,10 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install resource build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get -y install --no-install-recommends python3-pil
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: /opt/build.sh all
|
||||
|
@ -42,7 +42,7 @@ CMake configures the project according to variables you specify the command line
|
||||
**NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`|
|
||||
**CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
|
||||
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
|
||||
**BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [lv_img_conv](https://github.com/lvgl/lv_img_conv). |`-DBUILD_RESOURCES=1`
|
||||
**BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [python3-pil/pillow](https://pillow.readthedocs.io) module). |`-DBUILD_RESOURCES=1`
|
||||
**TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY-TFK5, MOY-TIN5, MOY-TON5, MOY-UNK`|`-DTARGET_DEVICE=PINETIME` (Default)
|
||||
|
||||
#### (\*) Note about **CMAKE_BUILD_TYPE**
|
||||
@ -98,4 +98,4 @@ Binary files are generated into the folder `src`:
|
||||
- **pinetime-mcuboot-app-image** : MCUBoot image of the firmware
|
||||
- **pinetime-mcuboot-app-dfu** : DFU file of the firmware
|
||||
|
||||
The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader**
|
||||
The same files are generated for **pinetime-recovery** and **pinetime-recovery-loader**
|
||||
|
@ -1,7 +1,13 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG NODE_MAJOR=20
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install -y ca-certificates curl gnupg \
|
||||
&& mkdir -p /etc/apt/keyrings \
|
||||
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
||||
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
|
||||
&& apt-get update -qq \
|
||||
&& apt-get install -y \
|
||||
# x86_64 / generic packages
|
||||
bash \
|
||||
@ -9,13 +15,14 @@ RUN apt-get update -qq \
|
||||
cmake \
|
||||
git \
|
||||
make \
|
||||
nodejs \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-pil \
|
||||
python-is-python3 \
|
||||
tar \
|
||||
unzip \
|
||||
wget \
|
||||
curl \
|
||||
# aarch64 packages
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
@ -28,8 +35,6 @@ RUN apt-get update -qq \
|
||||
libpango-1.0-0 \
|
||||
ibpango1.0-dev \
|
||||
libpangocairo-1.0-0 \
|
||||
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
|
||||
|
||||
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
|
||||
@ -39,10 +44,6 @@ RUN pip3 install -Iv cryptography==3.3
|
||||
RUN pip3 install cbor
|
||||
RUN npm i lv_font_conv@1.5.2 -g
|
||||
|
||||
RUN npm i ts-node@10.9.1 -g
|
||||
RUN npm i @swc/core -g
|
||||
RUN npm i lv_img_conv@0.3.0 -g
|
||||
|
||||
# build.sh knows how to compile
|
||||
COPY build.sh /opt/
|
||||
|
||||
|
@ -404,7 +404,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Clouds && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header);
|
||||
}
|
||||
}
|
||||
@ -415,7 +416,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Obscuration && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header);
|
||||
}
|
||||
}
|
||||
@ -426,7 +428,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Precipitation && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header);
|
||||
}
|
||||
}
|
||||
@ -437,7 +440,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Wind && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header);
|
||||
}
|
||||
}
|
||||
@ -448,7 +452,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Temperature && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header);
|
||||
}
|
||||
}
|
||||
@ -459,7 +464,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Humidity && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header);
|
||||
}
|
||||
}
|
||||
@ -470,7 +476,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Pressure && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header);
|
||||
}
|
||||
}
|
||||
@ -481,7 +488,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Location && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header);
|
||||
}
|
||||
}
|
||||
@ -492,7 +500,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::AirQuality && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"file": "JetBrainsMono-Regular.ttf",
|
||||
"range": "0x25, 0x2b, 0x2d, 0x30-0x3a"
|
||||
"range": "0x25, 0x2b, 0x2d, 0x30-0x3a, 0x4b-0x4d, 0x66, 0x69, 0x6b, 0x6d, 0x74"
|
||||
}
|
||||
],
|
||||
"bpp": 1,
|
||||
|
@ -203,19 +203,21 @@ Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navServi
|
||||
lv_obj_align(imgFlag, nullptr, LV_ALIGN_CENTER, 0, -60);
|
||||
|
||||
txtNarrative = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_BREAK);
|
||||
lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_DOT);
|
||||
lv_obj_set_width(txtNarrative, LV_HOR_RES);
|
||||
lv_obj_set_height(txtNarrative, 80);
|
||||
lv_label_set_text_static(txtNarrative, "Navigation");
|
||||
lv_label_set_align(txtNarrative, LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 10);
|
||||
lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 30);
|
||||
|
||||
txtManDist = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_long_mode(txtManDist, LV_LABEL_LONG_BREAK);
|
||||
lv_obj_set_style_local_text_color(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
|
||||
lv_obj_set_style_local_text_font(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
|
||||
lv_obj_set_width(txtManDist, LV_HOR_RES);
|
||||
lv_label_set_text_static(txtManDist, "--M");
|
||||
lv_label_set_align(txtManDist, LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 60);
|
||||
lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 90);
|
||||
|
||||
// Route Progress
|
||||
barProgress = lv_bar_create(lv_scr_act(), nullptr);
|
||||
|
@ -3,8 +3,8 @@ find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED
|
||||
HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
|
||||
message(STATUS "Using ${LV_FONT_CONV} to generate font files")
|
||||
|
||||
find_program(LV_IMG_CONV "lv_img_conv" NO_CACHE REQUIRED
|
||||
HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
|
||||
find_program(LV_IMG_CONV "lv_img_conv.py" NO_CACHE REQUIRED
|
||||
HINTS "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
message(STATUS "Using ${LV_IMG_CONV} to generate font files")
|
||||
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
||||
|
@ -11,6 +11,9 @@ import subprocess
|
||||
|
||||
def gen_lvconv_line(lv_img_conv: str, dest: str, color_format: str, output_format: str, binary_format: str, sources: str):
|
||||
args = [lv_img_conv, sources, '--force', '--output-file', dest, '--color-format', color_format, '--output-format', output_format, '--binary-format', binary_format]
|
||||
if lv_img_conv.endswith(".py"):
|
||||
# lv_img_conv is a python script, call with current python executable
|
||||
args = [sys.executable] + args
|
||||
|
||||
return args
|
||||
|
||||
|
193
src/resources/lv_img_conv.py
Executable file
193
src/resources/lv_img_conv.py
Executable file
@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import pathlib
|
||||
import sys
|
||||
import decimal
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def classify_pixel(value, bits):
|
||||
def round_half_up(v):
|
||||
"""python3 implements "propper" "banker's rounding" by rounding to the nearest
|
||||
even number. Javascript rounds to the nearest integer.
|
||||
To have the same output as the original JavaScript implementation add a custom
|
||||
rounding function, which does "school" rounding (to the nearest integer).
|
||||
|
||||
see: https://stackoverflow.com/questions/43851273/how-to-round-float-0-5-up-to-1-0-while-still-rounding-0-45-to-0-0-as-the-usual
|
||||
"""
|
||||
return int(decimal.Decimal(v).quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP))
|
||||
tmp = 1 << (8 - bits)
|
||||
val = round_half_up(value / tmp) * tmp
|
||||
if val < 0:
|
||||
val = 0
|
||||
return val
|
||||
|
||||
|
||||
def test_classify_pixel():
|
||||
# test difference between round() and round_half_up()
|
||||
assert classify_pixel(18, 5) == 16
|
||||
# school rounding 4.5 to 5, but banker's rounding 4.5 to 4
|
||||
assert classify_pixel(18, 6) == 20
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("img",
|
||||
help="Path to image to convert to C header file")
|
||||
parser.add_argument("-o", "--output-file",
|
||||
help="output file path (for single-image conversion)",
|
||||
required=True)
|
||||
parser.add_argument("-f", "--force",
|
||||
help="allow overwriting the output file",
|
||||
action="store_true")
|
||||
parser.add_argument("-i", "--image-name",
|
||||
help="name of image structure (not implemented)")
|
||||
parser.add_argument("-c", "--color-format",
|
||||
help="color format of image",
|
||||
default="CF_TRUE_COLOR_ALPHA",
|
||||
choices=[
|
||||
"CF_ALPHA_1_BIT", "CF_ALPHA_2_BIT", "CF_ALPHA_4_BIT",
|
||||
"CF_ALPHA_8_BIT", "CF_INDEXED_1_BIT", "CF_INDEXED_2_BIT", "CF_INDEXED_4_BIT",
|
||||
"CF_INDEXED_8_BIT", "CF_RAW", "CF_RAW_CHROMA", "CF_RAW_ALPHA",
|
||||
"CF_TRUE_COLOR", "CF_TRUE_COLOR_ALPHA", "CF_TRUE_COLOR_CHROMA", "CF_RGB565A8",
|
||||
],
|
||||
required=True)
|
||||
parser.add_argument("-t", "--output-format",
|
||||
help="output format of image",
|
||||
default="bin", # default in original is 'c'
|
||||
choices=["c", "bin"])
|
||||
parser.add_argument("--binary-format",
|
||||
help="binary color format (needed if output-format is binary)",
|
||||
default="ARGB8565_RBSWAP",
|
||||
choices=["ARGB8332", "ARGB8565", "ARGB8565_RBSWAP", "ARGB8888"])
|
||||
parser.add_argument("-s", "--swap-endian",
|
||||
help="swap endian of image (not implemented)",
|
||||
action="store_true")
|
||||
parser.add_argument("-d", "--dither",
|
||||
help="enable dither (not implemented)",
|
||||
action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
img_path = pathlib.Path(args.img)
|
||||
out = pathlib.Path(args.output_file)
|
||||
if not img_path.is_file():
|
||||
print(f"Input file is missing: '{args.img}'")
|
||||
return 1
|
||||
print(f"Beginning conversion of {args.img}")
|
||||
if out.exists():
|
||||
if args.force:
|
||||
print(f"overwriting {args.output_file}")
|
||||
else:
|
||||
pritn(f"Error: refusing to overwrite {args.output_file} without -f specified.")
|
||||
return 1
|
||||
out.touch()
|
||||
|
||||
# only implemented the bare minimum, everything else is not implemented
|
||||
if args.color_format not in ["CF_INDEXED_1_BIT", "CF_TRUE_COLOR_ALPHA"]:
|
||||
raise NotImplementedError(f"argument --color-format '{args.color_format}' not implemented")
|
||||
if args.output_format != "bin":
|
||||
raise NotImplementedError(f"argument --output-format '{args.output_format}' not implemented")
|
||||
if args.binary_format not in ["ARGB8565_RBSWAP", "ARGB8888"]:
|
||||
raise NotImplementedError(f"argument --binary-format '{args.binary_format}' not implemented")
|
||||
if args.image_name:
|
||||
raise NotImplementedError(f"argument --image-name not implemented")
|
||||
if args.swap_endian:
|
||||
raise NotImplementedError(f"argument --swap-endian not implemented")
|
||||
if args.dither:
|
||||
raise NotImplementedError(f"argument --dither not implemented")
|
||||
|
||||
# open image using Pillow
|
||||
img = Image.open(img_path)
|
||||
img_height = img.height
|
||||
img_width = img.width
|
||||
if args.color_format == "CF_TRUE_COLOR_ALPHA" and args.binary_format == "ARGB8888":
|
||||
buf = bytearray(img_height*img_width*4) # 4 bytes (32 bit) per pixel
|
||||
for y in range(img_height):
|
||||
for x in range(img_width):
|
||||
i = (y*img_width + x)*4 # buffer-index
|
||||
pixel = img.getpixel((x,y))
|
||||
r, g, b, a = pixel
|
||||
buf[i + 0] = r
|
||||
buf[i + 1] = g
|
||||
buf[i + 2] = b
|
||||
buf[i + 3] = a
|
||||
|
||||
elif args.color_format == "CF_TRUE_COLOR_ALPHA" and args.binary_format == "ARGB8565_RBSWAP":
|
||||
buf = bytearray(img_height*img_width*3) # 3 bytes (24 bit) per pixel
|
||||
for y in range(img_height):
|
||||
for x in range(img_width):
|
||||
i = (y*img_width + x)*3 # buffer-index
|
||||
pixel = img.getpixel((x,y))
|
||||
r_act = classify_pixel(pixel[0], 5)
|
||||
g_act = classify_pixel(pixel[1], 6)
|
||||
b_act = classify_pixel(pixel[2], 5)
|
||||
a = pixel[3]
|
||||
r_act = min(r_act, 0xF8)
|
||||
g_act = min(g_act, 0xFC)
|
||||
b_act = min(b_act, 0xF8)
|
||||
c16 = ((r_act) << 8) | ((g_act) << 3) | ((b_act) >> 3) # RGR565
|
||||
buf[i + 0] = (c16 >> 8) & 0xFF
|
||||
buf[i + 1] = c16 & 0xFF
|
||||
buf[i + 2] = a
|
||||
|
||||
elif args.color_format == "CF_INDEXED_1_BIT": # ignore binary format, use color format as binary format
|
||||
w = img_width >> 3
|
||||
if img_width & 0x07:
|
||||
w+=1
|
||||
max_p = w * (img_height-1) + ((img_width-1) >> 3) + 8 # +8 for the palette
|
||||
buf = bytearray(max_p+1)
|
||||
|
||||
for y in range(img_height):
|
||||
for x in range(img_width):
|
||||
c, a = img.getpixel((x,y))
|
||||
p = w * y + (x >> 3) + 8 # +8 for the palette
|
||||
buf[p] |= (c & 0x1) << (7 - (x & 0x7))
|
||||
# write palette information, for indexed-1-bit we need palette with two values
|
||||
# write 8 palette bytes
|
||||
buf[0] = 0
|
||||
buf[1] = 0
|
||||
buf[2] = 0
|
||||
buf[3] = 0
|
||||
# Normally there is much math behind this, but for the current use case this is close enough
|
||||
# only needs to be more complicated if we have more than 2 colors in the palette
|
||||
buf[4] = 255
|
||||
buf[5] = 255
|
||||
buf[6] = 255
|
||||
buf[7] = 255
|
||||
else:
|
||||
# raise just to be sure
|
||||
raise NotImplementedError(f"args.color_format '{args.color_format}' with args.binary_format '{args.binary_format}' not implemented")
|
||||
|
||||
# write header
|
||||
match args.color_format:
|
||||
case "CF_TRUE_COLOR_ALPHA":
|
||||
lv_cf = 5
|
||||
case "CF_INDEXED_1_BIT":
|
||||
lv_cf = 7
|
||||
case _:
|
||||
# raise just to be sure
|
||||
raise NotImplementedError(f"args.color_format '{args.color_format}' not implemented")
|
||||
header_32bit = lv_cf | (img_width << 10) | (img_height << 21)
|
||||
buf_out = bytearray(4 + len(buf))
|
||||
buf_out[0] = header_32bit & 0xFF
|
||||
buf_out[1] = (header_32bit & 0xFF00) >> 8
|
||||
buf_out[2] = (header_32bit & 0xFF0000) >> 16
|
||||
buf_out[3] = (header_32bit & 0xFF000000) >> 24
|
||||
buf_out[4:] = buf
|
||||
|
||||
# write byte buffer to file
|
||||
with open(out, "wb") as f:
|
||||
f.write(buf_out)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if "--test" in sys.argv:
|
||||
# run small set of tests and exit
|
||||
print("running tests")
|
||||
test_classify_pixel()
|
||||
print("success!")
|
||||
sys.exit(0)
|
||||
# run normal program
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue
Block a user