Cleanup: remove unused icon utilities and make convenience target

Remove utilities to assist in creation of the now removed `*.dat` icons.

Pull Request: https://projects.blender.org/blender/blender/pulls/123837
This commit is contained in:
Campbell Barton 2024-06-27 18:28:32 +02:00 committed by Harley Acheson
parent 9584f8fb55
commit a8fae77f10
11 changed files with 0 additions and 1288 deletions

@ -96,15 +96,6 @@ Spell Checkers
Utilities
Not associated with building Blender.
* icons:
Updates PNG icons from SVG files.
Optionally pass in variables: 'BLENDER_BIN', 'INKSCAPE_BIN'
otherwise default paths are used.
Example
make icons INKSCAPE_BIN=/path/to/inkscape
* icons_geom:
Updates Geometry icons from BLEND file.
@ -565,11 +556,6 @@ source_archive_complete: .FORCE
# This assumes CMake is still using a default `PACKAGE_DIR` variable:
@$(PYTHON) ./build_files/utils/make_source_archive.py --include-packages "$(BUILD_DIR)/source_archive/packages"
icons: .FORCE
@BLENDER_BIN=$(BLENDER_BIN) "$(BLENDER_DIR)/release/datafiles/blender_icons_update.py"
"$(BLENDER_DIR)/release/datafiles/prvicons_update.py"
"$(BLENDER_DIR)/release/datafiles/alert_icons_update.py"
icons_geom: .FORCE
@BLENDER_BIN=$(BLENDER_BIN) \
"$(BLENDER_DIR)/release/datafiles/blender_icons_geom_update.py"

@ -1040,91 +1040,6 @@ function(data_to_c_simple
set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE)
endfunction()
# Function for converting pixmap directory to a '.png' and then a '.c' file.
function(data_to_c_simple_icons
path_from icon_prefix icon_names
list_to_add
)
# Conversion steps
# path_from -> _file_from -> _file_to
# foo/*.dat -> foo.png -> foo.png.c
get_filename_component(_path_from_abs ${path_from} ABSOLUTE)
# remove ../'s
get_filename_component(_file_from ${CMAKE_CURRENT_BINARY_DIR}/${path_from}.png REALPATH)
get_filename_component(_file_to ${CMAKE_CURRENT_BINARY_DIR}/${path_from}.png.c REALPATH)
list(APPEND ${list_to_add} ${_file_to})
set(${list_to_add} ${${list_to_add}} PARENT_SCOPE)
get_filename_component(_file_to_path ${_file_to} PATH)
# Construct a list of absolute paths from input
set(_icon_files)
foreach(_var ${icon_names})
list(APPEND _icon_files "${_path_from_abs}/${icon_prefix}${_var}.dat")
endforeach()
add_custom_command(
OUTPUT ${_file_from} ${_file_to}
COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path}
# COMMAND python3 ${CMAKE_SOURCE_DIR}/source/blender/datatoc/datatoc_icon.py
# ${_path_from_abs} ${_file_from}
COMMAND "$<TARGET_FILE:datatoc_icon>" ${_path_from_abs} ${_file_from}
COMMAND "$<TARGET_FILE:datatoc>" ${_file_from} ${_file_to}
DEPENDS
${_icon_files}
datatoc_icon
datatoc
# could be an arg but for now we only create icons depending on UI_icons.hh
${CMAKE_SOURCE_DIR}/source/blender/editors/include/UI_icons.hh
)
set_source_files_properties(${_file_from} ${_file_to} PROPERTIES GENERATED TRUE)
endfunction()
# XXX Not used for now...
function(svg_to_png
file_from
file_to
dpi
list_to_add
)
# remove ../'s
get_filename_component(_file_from ${CMAKE_CURRENT_SOURCE_DIR}/${file_from} REALPATH)
get_filename_component(_file_to ${CMAKE_CURRENT_SOURCE_DIR}/${file_to} REALPATH)
list(APPEND ${list_to_add} ${_file_to})
set(${list_to_add} ${${list_to_add}} PARENT_SCOPE)
find_program(INKSCAPE_EXE inkscape)
mark_as_advanced(INKSCAPE_EXE)
if(INKSCAPE_EXE)
if(APPLE)
# in OS X app bundle, the binary is a shim that doesn't take any
# command line arguments, replace it with the actual binary
string(REPLACE "MacOS/Inkscape" "Resources/bin/inkscape" INKSCAPE_REAL_EXE ${INKSCAPE_EXE})
if(EXISTS "${INKSCAPE_REAL_EXE}")
set(INKSCAPE_EXE ${INKSCAPE_REAL_EXE})
endif()
endif()
add_custom_command(
OUTPUT ${_file_to}
COMMAND ${INKSCAPE_EXE}
${_file_from} --export-dpi=${dpi} --without-gui --export-png=${_file_to}
DEPENDS ${_file_from} ${INKSCAPE_EXE}
)
else()
message(WARNING "Inkscape not found, could not re-generate ${_file_to} from ${_file_from}!")
endif()
endfunction()
function(msgfmt_simple
file_from
list_to_add

@ -1,32 +0,0 @@
if NOT EXIST %PYTHON% (
echo python not found, required for this operation
exit /b 1
)
call "%~dp0\find_inkscape.cmd"
if EXIST "%INKSCAPE_BIN%" (
goto detect_inkscape_done
)
echo unable to locate inkscape, run "set inkscape_BIN=full_path_to_inkscape.exe"
exit /b 1
:detect_inkscape_done
call "%~dp0\find_blender.cmd"
if EXIST "%BLENDER_BIN%" (
goto detect_blender_done
)
echo unable to locate blender, run "set BLENDER_BIN=full_path_to_blender.exe"
exit /b 1
:detect_blender_done
%PYTHON% -B %BLENDER_DIR%\release\datafiles\blender_icons_update.py
%PYTHON% -B %BLENDER_DIR%\release\datafiles\prvicons_update.py
%PYTHON% -B %BLENDER_DIR%\release\datafiles\alert_icons_update.py
:EOF

@ -103,9 +103,6 @@ if NOT "%1" == "" (
set FORMAT=1
set FORMAT_ARGS=%2 %3 %4 %5 %6 %7 %8 %9
goto EOF
) else if "%1" == "icons" (
set ICONS=1
goto EOF
) else if "%1" == "icons_geom" (
set ICONS_GEOM=1
goto EOF

@ -78,11 +78,6 @@ call "%BLENDER_DIR%\build_files\windows\set_build_dir.cmd"
:convenience_targets
if "%ICONS%" == "1" (
call "%BLENDER_DIR%\build_files\windows\icons.cmd"
goto EOF
)
if "%ICONS_GEOM%" == "1" (
call "%BLENDER_DIR%\build_files\windows\icons_geom.cmd"
goto EOF

@ -1,106 +0,0 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2014-2022 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# This script updates icons from the SVG file
import os
import subprocess
import sys
from typing import (
Dict,
List,
Optional,
Sequence,
Tuple,
)
def run(cmd: Sequence[str], *, env: Optional[Dict[str, str]] = None) -> None:
print(" ", " ".join(cmd))
subprocess.check_call(cmd, env=env)
BASEDIR = os.path.abspath(os.path.dirname(__file__))
env = {}
# Developers may have ASAN enabled, avoid non-zero exit codes.
env["ASAN_OPTIONS"] = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "")
# These NEED to be set on windows for python to initialize properly.
if sys.platform[:3] == "win":
env["PATHEXT"] = os.environ.get("PATHEXT", "")
env["SystemDrive"] = os.environ.get("SystemDrive", "")
env["SystemRoot"] = os.environ.get("SystemRoot", "")
if not (inkscape_bin := os.environ.get("INKSCAPE_BIN")):
if sys.platform == 'darwin':
inkscape_bin = '/Applications/Inkscape.app/Contents/MacOS/inkscape'
else:
inkscape_bin = "inkscape"
blender_bin = os.environ.get("BLENDER_BIN", "blender")
cmd: Tuple[str, ...] = (
inkscape_bin,
os.path.join(BASEDIR, "blender_icons.svg"),
"--export-width=602",
"--export-height=640",
"--export-type=png",
"--export-filename=" + os.path.join(BASEDIR, "blender_icons16.png"),
)
run(cmd, env=env)
cmd = (
inkscape_bin,
os.path.join(BASEDIR, "blender_icons.svg"),
"--export-width=1204",
"--export-height=1280",
"--export-type=png",
"--export-filename=" + os.path.join(BASEDIR, "blender_icons32.png"),
)
run(cmd, env=env)
# For testing it can be good to clear all old
# rm ./blender_icons16/*.dat
# rm ./blender_icons32/*.dat
datatoc_icon_split_py = os.path.join(BASEDIR, "..", "..", "source", "blender", "datatoc", "datatoc_icon_split.py")
# create .dat pixmaps (which are stored in git)
cmd = (
blender_bin, "--background", "--factory-startup",
"--python", datatoc_icon_split_py, "--",
"--image=" + os.path.join(BASEDIR, "blender_icons16.png"),
"--output=" + os.path.join(BASEDIR, "blender_icons16"),
"--output_prefix=icon16_",
"--name_style=UI_ICONS",
"--parts_x", "26", "--parts_y", "30",
"--minx", "3", "--maxx", "53", "--miny", "3", "--maxy", "8",
"--minx_icon", "2", "--maxx_icon", "2", "--miny_icon", "2", "--maxy_icon", "2",
"--spacex_icon", "1", "--spacey_icon", "1",
)
run(cmd, env=env)
cmd = (
blender_bin, "--background", "--factory-startup",
"--python", datatoc_icon_split_py, "--",
"--image=" + os.path.join(BASEDIR, "blender_icons32.png"),
"--output=" + os.path.join(BASEDIR, "blender_icons32"),
"--output_prefix=icon32_",
"--name_style=UI_ICONS",
"--parts_x", "26", "--parts_y", "30",
"--minx", "6", "--maxx", "106", "--miny", "6", "--maxy", "16",
"--minx_icon", "4", "--maxx_icon", "4", "--miny_icon", "4", "--maxy_icon", "4",
"--spacex_icon", "2", "--spacey_icon", "2",
)
run(cmd, env=env)
os.remove(os.path.join(BASEDIR, "blender_icons16.png"))
os.remove(os.path.join(BASEDIR, "blender_icons32.png"))
# For testing, if we want the PNG of each image
# ./datatoc_icon_split_to_png.py ./blender_icons16/*.dat
# ./datatoc_icon_split_to_png.py ./blender_icons32/*.dat

@ -11,42 +11,3 @@ set(SRC
# SRC_DNA_INC is defined in the parent dir
add_executable(datatoc ${SRC})
# -----------------------------------------------------------------------------
# Build datatoc_icon executable
if(NOT WITH_HEADLESS)
set(SRC
datatoc_icon.cc
)
setup_platform_linker_flags(datatoc)
if(WIN32)
include_directories(
../blenlib
../../../intern/utfconv
)
# for winstuff_dir.cc
add_definitions(-DUSE_STANDALONE)
list(APPEND SRC
../blenlib/intern/winstuff_dir.cc
../../../intern/utfconv/utfconv.cc
)
endif()
include_directories(${PNG_INCLUDE_DIRS})
add_executable(datatoc_icon ${SRC})
setup_platform_linker_flags(datatoc_icon)
target_link_libraries(datatoc_icon ${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
# PNG library uses pow() and floow(), so seems -lm is required for proper
# working binary.
if(UNIX AND NOT APPLE)
target_link_libraries(datatoc_icon m)
endif()
endif()

@ -1,496 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup datatoc
*/
#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
/* for bool */
#include "../blenlib/BLI_sys_types.h"
/* for DIR */
#if !defined(WIN32) || defined(FREEWINDOWS)
# include <dirent.h>
#endif
#include <png.h>
/* for Win32 DIR functions */
#ifdef WIN32
# include "../blenlib/BLI_winstuff.h"
#endif
#ifdef WIN32
# define SEP '\\'
#else
# define SEP '/'
#endif
/* -------------------------------------------------------------------- */
/** \name Endian Defines
* \{ */
#define L_ENDIAN 1
#define B_ENDIAN 0
#ifdef __BIG_ENDIAN__
# define ENDIAN_ORDER B_ENDIAN
#else
# define ENDIAN_ORDER L_ENDIAN
#endif
/** \} */
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
static bool path_test_extension(const char *filepath, const char *ext)
{
const size_t a = strlen(filepath);
const size_t b = strlen(ext);
return !(a == 0 || b == 0 || b >= a) && (strcmp(ext, filepath + a - b) == 0);
}
static void endian_switch_uint32(uint *val)
{
uint tval = *val;
*val = (tval >> 24) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | (tval << 24);
}
static const char *path_slash_rfind(const char *path)
{
const char *const lfslash = strrchr(path, '/');
const char *const lbslash = strrchr(path, '\\');
if (!lfslash) {
return lbslash;
}
if (!lbslash) {
return lfslash;
}
return (lfslash > lbslash) ? lfslash : lbslash;
}
static const char *path_basename(const char *path)
{
const char *const filename = path_slash_rfind(path);
return filename ? filename + 1 : path;
}
static bool path_join(char *filepath,
size_t filepath_maxncpy,
const char *dirpath,
const char *filename)
{
int dirpath_len = strlen(dirpath);
if (dirpath_len && dirpath[dirpath_len - 1] == SEP) {
dirpath_len--;
}
const int filename_len = strlen(filename);
if (dirpath_len + 1 + filename_len + 1 > filepath_maxncpy) {
return false;
}
memcpy(filepath, dirpath, dirpath_len);
filepath[dirpath_len] = SEP;
memcpy(filepath + dirpath_len + 1, filename, filename_len + 1);
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Write a PNG from RGBA Pixels
* \{ */
static bool write_png(const char *filepath, const uint *pixels, const int width, const int height)
{
png_structp png_ptr;
png_infop info_ptr;
png_bytepp row_pointers = nullptr;
FILE *fp;
const int bytesperpixel = 4;
const int compression = 9;
int i;
fp = fopen(filepath, "wb");
if (fp == nullptr) {
printf("%s: Cannot open file for writing '%s'\n", __func__, filepath);
return false;
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (png_ptr == nullptr) {
printf("%s: Cannot png_create_write_struct for file: '%s'\n", __func__, filepath);
fclose(fp);
return false;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) {
png_destroy_write_struct(&png_ptr, (png_infopp) nullptr);
printf("%s: Cannot png_create_info_struct for file: '%s'\n", __func__, filepath);
fclose(fp);
return false;
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
printf("%s: Cannot setjmp for file: '%s'\n", __func__, filepath);
fclose(fp);
return false;
}
/* write the file */
png_init_io(png_ptr, fp);
png_set_compression_level(png_ptr, compression);
/* png image settings */
png_set_IHDR(png_ptr,
info_ptr,
width,
height,
8,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
/* write the file header information */
png_write_info(png_ptr, info_ptr);
if (ENDIAN_ORDER == L_ENDIAN) {
png_set_swap(png_ptr);
}
/* allocate memory for an array of row-pointers */
row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep));
if (row_pointers == nullptr) {
printf("%s: Cannot allocate row-pointers array for file '%s'\n", __func__, filepath);
png_destroy_write_struct(&png_ptr, &info_ptr);
if (fp) {
fclose(fp);
}
return false;
}
/* set the individual row-pointers to point at the correct offsets */
for (i = 0; i < height; i++) {
row_pointers[height - 1 - i] = (png_bytep)(((const uchar *)pixels) +
(i * width) * bytesperpixel * sizeof(uchar));
}
/* write out the entire image data in one call */
png_write_image(png_ptr, row_pointers);
/* write the additional chunks to the PNG file (not really needed) */
png_write_end(png_ptr, info_ptr);
/* clean up */
free(row_pointers);
png_destroy_write_struct(&png_ptr, &info_ptr);
fflush(fp);
fclose(fp);
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Merge Icon-Data from Files
* \{ */
struct IconHead {
uint icon_w, icon_h;
uint orig_x, orig_y;
uint canvas_w, canvas_h;
};
struct IconInfo {
IconHead head;
char *file_name;
};
struct IconMergeContext {
/* Information about all icons read from disk.
* Is used for sanity checks like prevention of two files defining icon for
* the same position on canvas. */
int num_read_icons;
IconInfo *read_icons;
};
static void icon_merge_context_init(IconMergeContext *context)
{
context->num_read_icons = 0;
context->read_icons = nullptr;
}
/* Get icon information from the context which matches given icon head.
* Is used to check whether icon is re-defined, and to provide useful information about which
* files are conflicting. */
static IconInfo *icon_merge_context_info_for_icon_head(IconMergeContext *context,
const IconHead *icon_head)
{
if (context->read_icons == nullptr) {
return nullptr;
}
for (int i = 0; i < context->num_read_icons; i++) {
IconInfo *read_icon_info = &context->read_icons[i];
const IconHead *read_icon_head = &read_icon_info->head;
if (read_icon_head->orig_x == icon_head->orig_x && read_icon_head->orig_y == icon_head->orig_y)
{
return read_icon_info;
}
}
return nullptr;
}
static void icon_merge_context_register_icon(IconMergeContext *context,
const char *file_name,
const IconHead *icon_head)
{
context->read_icons = static_cast<IconInfo *>(
realloc(context->read_icons, sizeof(IconInfo) * (context->num_read_icons + 1)));
IconInfo *icon_info = &context->read_icons[context->num_read_icons];
icon_info->head = *icon_head;
icon_info->file_name = strdup(path_basename(file_name));
context->num_read_icons++;
}
static void icon_merge_context_free(IconMergeContext *context)
{
if (context->read_icons != nullptr) {
for (int i = 0; i < context->num_read_icons; i++) {
free(context->read_icons[i].file_name);
}
free(context->read_icons);
}
}
static bool icon_decode_head(FILE *f_src, IconHead *r_head)
{
if (fread(r_head, 1, sizeof(*r_head), f_src) == sizeof(*r_head)) {
if (ENDIAN_ORDER == B_ENDIAN) {
endian_switch_uint32(&r_head->icon_w);
endian_switch_uint32(&r_head->icon_h);
endian_switch_uint32(&r_head->orig_x);
endian_switch_uint32(&r_head->orig_y);
endian_switch_uint32(&r_head->canvas_w);
endian_switch_uint32(&r_head->canvas_h);
}
return true;
}
return false;
}
static bool icon_decode(FILE *f_src, IconHead *r_head, uint **r_pixels)
{
uint *pixels;
uint pixels_size;
if (!icon_decode_head(f_src, r_head)) {
printf("%s: failed to read header\n", __func__);
return false;
}
pixels_size = sizeof(char[4]) * r_head->icon_w * r_head->icon_h;
pixels = static_cast<uint *>(malloc(pixels_size));
if (pixels == nullptr) {
printf("%s: failed to allocate pixels\n", __func__);
return false;
}
if (fread(pixels, 1, pixels_size, f_src) != pixels_size) {
printf("%s: failed to read pixels\n", __func__);
free(pixels);
return false;
}
*r_pixels = pixels;
return true;
}
static bool icon_read(const char *file_src, IconHead *r_head, uint **r_pixels)
{
FILE *f_src;
bool success;
f_src = fopen(file_src, "rb");
if (f_src == nullptr) {
printf("%s: failed to open '%s'\n", __func__, file_src);
return false;
}
success = icon_decode(f_src, r_head, r_pixels);
fclose(f_src);
return success;
}
static bool icon_merge(IconMergeContext *context,
const char *file_src,
uint32_t **r_pixels_canvas,
uint *r_canvas_w,
uint *r_canvas_h)
{
IconHead head;
uint *pixels;
uint x, y;
/* canvas */
uint32_t *pixels_canvas;
uint canvas_w, canvas_h;
if (!icon_read(file_src, &head, &pixels)) {
return false;
}
const IconInfo *read_icon_info = icon_merge_context_info_for_icon_head(context, &head);
if (read_icon_info != nullptr) {
printf(
"Conflicting icon files %s and %s\n", path_basename(file_src), read_icon_info->file_name);
free(pixels);
return false;
}
icon_merge_context_register_icon(context, file_src, &head);
if (*r_canvas_w == 0) {
/* init once */
*r_canvas_w = head.canvas_w;
*r_canvas_h = head.canvas_h;
*r_pixels_canvas = static_cast<uint32_t *>(
calloc(1, (head.canvas_w * head.canvas_h) * sizeof(uint32_t)));
}
canvas_w = *r_canvas_w;
canvas_h = *r_canvas_h;
pixels_canvas = *r_pixels_canvas;
assert(head.canvas_w == canvas_w);
assert(head.canvas_h == canvas_h);
for (x = 0; x < head.icon_w; x++) {
for (y = 0; y < head.icon_h; y++) {
uint pixel;
uint dst_x, dst_y;
uint pixel_xy_dst;
/* get pixel */
pixel = pixels[(y * head.icon_w) + x];
/* set pixel */
dst_x = head.orig_x + x;
dst_y = head.orig_y + y;
pixel_xy_dst = (dst_y * canvas_w) + dst_x;
assert(pixel_xy_dst < (canvas_w * canvas_h));
pixels_canvas[pixel_xy_dst] = pixel;
}
}
free(pixels);
/* only for bounds check */
(void)canvas_h;
return true;
}
static bool icondir_to_png(const char *path_src, const char *file_dst)
{
/* Takes a path full of 'dat' files and writes out */
DIR *dir;
const dirent *fname;
char filepath[1024];
int found = 0, fail = 0;
IconMergeContext context;
uint32_t *pixels_canvas = nullptr;
uint canvas_w = 0, canvas_h = 0;
icon_merge_context_init(&context);
errno = 0;
dir = opendir(path_src);
if (dir == nullptr) {
printf(
"%s: failed to dir '%s', (%s)\n", __func__, path_src, errno ? strerror(errno) : "unknown");
return false;
}
while ((fname = readdir(dir)) != nullptr) {
if (path_test_extension(fname->d_name, ".dat")) {
if (!path_join(filepath, sizeof(filepath), path_src, fname->d_name)) {
printf("%s: path is too long (%s, %s)\n", __func__, path_src, fname->d_name);
return false;
}
if (icon_merge(&context, filepath, &pixels_canvas, &canvas_w, &canvas_h)) {
found++;
}
else {
fail++;
}
}
}
icon_merge_context_free(&context);
closedir(dir);
if (found == 0) {
printf("%s: dir '%s' has no icons\n", __func__, path_src);
}
if (fail != 0) {
printf("%s: dir '%s' failed %d icons\n", __func__, path_src, fail);
}
/* Write pixels. */
write_png(file_dst, pixels_canvas, canvas_w, canvas_h);
free(pixels_canvas);
return (fail == 0);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Main & Parse Arguments
* \{ */
int main(int argc, char **argv)
{
const char *path_src;
const char *file_dst;
if (argc < 3) {
printf("Usage: datatoc_icon <dir_icons> <data_icon_to.png>\n");
exit(1);
}
path_src = argv[1];
file_dst = argv[2];
return (icondir_to_png(path_src, file_dst) == true) ? 0 : 1;
}
/** \} */

@ -1,146 +0,0 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2014-2022 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
_IS_BIG_ENDIAN = (__import__("sys").byteorder != 'little')
def write_png(buf, width, height):
import zlib
import struct
# reverse the vertical line order and add null bytes at the start
width_byte_4 = width * 4
raw_data = b"".join(
b'\x00' + buf[span:span + width_byte_4]
for span in range((height - 1) * width * 4, -1, - width_byte_4)
)
def png_pack(png_tag, data):
chunk_head = png_tag + data
return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))
return b"".join([
b'\x89PNG\r\n\x1a\n',
png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
png_pack(b'IDAT', zlib.compress(raw_data, 9)),
png_pack(b'IEND', b'')])
def icon_decode_head(f_src):
import struct
# 2 ints
temp_data = f_src.read(4 * 2)
icon_w, icon_h = struct.unpack('<2I', temp_data)
temp_data = f_src.read(4 * 2)
orig_x, orig_y = struct.unpack('<2I', temp_data)
temp_data = f_src.read(4 * 2)
canvas_w, canvas_h = struct.unpack('<2I', temp_data)
return (icon_w, icon_h,
orig_x, orig_y,
canvas_w, canvas_h)
def icon_decode(f_src):
head = icon_decode_head(f_src)
(icon_w, icon_h,
orig_x, orig_y,
canvas_w, canvas_h) = head
# pixels
import array
pixels = f_src.read(icon_w * icon_h * 4)
pixels = array.array('I', pixels)
if _IS_BIG_ENDIAN:
pixels.byteswap()
return head, pixels
def icon_read(file_src):
with open(file_src, 'rb') as f_src:
head, pixels = icon_decode(f_src)
return head, pixels
def icon_merge(file_src, pixels_canvas, canvas_w, canvas_h):
""" Takes an icon filepath and merges into a pixel array
"""
head, pixels = icon_read(file_src)
(icon_w, icon_h,
orig_x, orig_y,
w_canvas_test, h_canvas_test) = head
assert w_canvas_test == canvas_w
assert h_canvas_test == canvas_h
for x in range(icon_w):
for y in range(icon_h):
# get pixel
pixel = pixels[(y * icon_w) + x]
# set pixel
dst_x = orig_x + x
dst_y = orig_y + y
pixels_canvas[(dst_y * canvas_w) + dst_x] = pixel
def icondir_to_png(path_src, file_dst):
""" Takes a path full of 'dat' files and writes out
"""
import os
import array
files = [os.path.join(path_src, f) for f in os.listdir(path_src) if f.endswith(".dat")]
# First check if we need to bother.
if os.path.exists(file_dst):
dst_time = os.path.getmtime(file_dst)
has_newer = False
for f in files:
if os.path.getmtime(f) > dst_time:
has_newer = True
break
if not has_newer:
return
with open(files[0], 'rb') as f_src:
(icon_w, icon_h,
orig_x, orig_y,
canvas_w, canvas_h) = icon_decode_head(f_src)
# load in pixel data
pixels_canvas = array.array('I', [0]) * (canvas_w * canvas_h)
for f in files:
icon_merge(f, pixels_canvas, canvas_w, canvas_h)
# write pixels
with open(file_dst, 'wb') as f_dst:
pixels_data = pixels_canvas.tobytes()
image_data = write_png(pixels_data, canvas_w, canvas_h)
f_dst.write(image_data)
def main_ex(argv):
import os
path_src = argv[-2].rstrip(os.sep)
file_dst = argv[-1]
icondir_to_png(path_src, file_dst)
def main():
import sys
main_ex(sys.argv)
if __name__ == "__main__":
main()

@ -1,302 +0,0 @@
# SPDX-FileCopyrightText: 2014-2022 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
"""
This script dices up PNG into small files to store in version control.
Example:
./blender.bin \
--background \
--python ./release/datafiles/icon_dice.py -- \
--image=./release/datafiles/blender_icons16.png \
--output=./release/datafiles/blender_icons16
--output_prefix=icon16_
--name_style=UI_ICONS
--parts_x 26 --parts_y 32 \
--minx=10 --maxx 10 --miny 10 --maxy 10
--minx_icon 2 --maxx_icon 2 --miny_icon 2 --maxy_icon 2 \
--spacex_icon 1 --spacey_icon 1
"""
import os
SOURCE_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
VERBOSE = False
def image_from_file__bpy(filepath):
import bpy
image = bpy.data.images.load(filepath)
image.reload()
pixel_w, pixel_h = image.size
pixels = image.pixels[:]
return pixels, pixel_w, pixel_h
def image_from_file(filepath):
"""
Return pixels, w, h from an image.
note: bpy import is ONLY used here.
"""
try:
import bpy
except ImportError:
bpy = None
if bpy is not None:
pixels, pixel_w, pixel_h = image_from_file__bpy(filepath)
# else:
# pixels, pixel_w, pixel_h = image_from_file__py(filepath)
return pixels, pixel_w, pixel_h
def write_subimage(sub_x, sub_y, sub_w, sub_h,
filepath,
pixels, pixel_w, pixel_h):
import struct
# first check if the icon is worth writing
is_fill = False
for y in range(sub_h):
for x in range(sub_w):
i = (sub_x + x) + ((sub_y + y) * pixel_w)
a = pixels[(i * 4) + 3]
if a != 0.0:
is_fill = True
break
if not is_fill:
# print("skipping:", filepath)
return
with open(filepath, 'wb') as f:
f.write(
struct.pack(
'<6I',
sub_w, sub_h,
sub_x, sub_y,
# redundant but including to maintain consistency
pixel_w, pixel_h,
))
for y in range(sub_h):
for x in range(sub_w):
i = (sub_x + x) + ((sub_y + y) * pixel_w)
rgba = pixels[(i * 4):(i * 4) + 4]
c = sum((int(p * 255) << (8 * i)) for i, p in enumerate(rgba))
f.write(struct.pack("<I", c))
_dice_icon_name_cache = {}
def dice_icon_name(
x, y, parts_x, parts_y,
name_style=None, prefix=""):
"""
How to name icons, this is mainly for what name we get in git,
the actual names don't really matter, its just nice to have the
name match up with something recognizable for commits.
"""
if name_style == 'UI_ICONS':
# Init on demand
if not _dice_icon_name_cache:
import re
count = 0
# Search for eg: DEF_ICON(BRUSH_NUDGE) --> BRUSH_NUDGE
re_icon = re.compile(r'^\s*DEF_ICON.*\(\s*([A-Za-z0-9_]+)\s*\).*$')
ui_icons_h = os.path.join(SOURCE_DIR, "source", "blender", "editors", "include", "UI_icons.hh")
with open(ui_icons_h, 'r', encoding="utf-8") as f:
for l in f:
match = re_icon.search(l)
if match:
if l.find('DEF_ICON_BLANK') == -1:
icon_name = match.group(1).lower()
print(icon_name)
_dice_icon_name_cache[count] = icon_name
count += 1
# ---- Done with icon cache
index = (y * parts_x) + x
if index not in _dice_icon_name_cache:
return None
icon_name = _dice_icon_name_cache[index]
# for debugging its handy to sort by number
# ~ id_str = "%03d_%s%s.dat" % (index, prefix, icon_name)
id_str = "%s%s.dat" % (prefix, icon_name)
elif name_style == "":
# flip so icons are numbered from top-left
# because new icons will be added at the bottom
y_flip = parts_y - (y + 1)
id_str = "%s%02xx%02x.dat" % (prefix, x, y_flip)
else:
raise Exception("Invalid '--name_style' arg")
return id_str
def dice(
filepath, output, output_prefix, name_style,
parts_x, parts_y,
minx, miny, maxx, maxy,
minx_icon, miny_icon, maxx_icon, maxy_icon,
spacex_icon, spacey_icon,
):
is_simple = (max(
minx, miny, maxx, maxy,
minx_icon, miny_icon, maxx_icon, maxy_icon,
spacex_icon, spacey_icon) == 0)
pixels, pixel_w, pixel_h = image_from_file(filepath)
if not (pixel_w and pixel_h):
print("Image not found %r!" % filepath)
return
if not os.path.exists(output):
os.mkdir(output)
if is_simple:
pixels_w_clip = pixel_w
pixels_h_clip = pixel_h
icon_w = pixels_w_clip // parts_x
icon_h = pixels_h_clip // parts_y
icon_w_clip = icon_w
icon_h_clip = icon_h
else:
pixels_w_clip = pixel_w - (minx + maxx)
pixels_h_clip = pixel_h - (miny + maxy)
icon_w = (pixels_w_clip - ((parts_x - 1) * spacex_icon)) // parts_x
icon_h = (pixels_h_clip - ((parts_y - 1) * spacey_icon)) // parts_y
icon_w_clip = icon_w - (minx_icon + maxx_icon)
icon_h_clip = icon_h - (miny_icon + maxy_icon)
print(pixel_w, pixel_h, icon_w, icon_h)
for x in range(parts_x):
for y in range(parts_y):
id_str = dice_icon_name(
x, y,
parts_x, parts_y,
name_style=name_style, prefix=output_prefix,
)
if not id_str:
continue
filepath = os.path.join(output, id_str)
if VERBOSE:
print(" writing:", filepath)
# simple, no margins
if is_simple:
sub_x = x * icon_w
sub_y = y * icon_h
else:
sub_x = minx + ((x * (icon_w + spacex_icon)) + minx_icon)
sub_y = miny + ((y * (icon_h + spacey_icon)) + miny_icon)
write_subimage(sub_x, sub_y, icon_w_clip, icon_h_clip,
filepath,
pixels, pixel_w, pixel_h)
def main():
import sys
import argparse
epilog = "Run this after updating the SVG file"
argv = sys.argv
if "--" not in argv:
argv = []
else:
argv = argv[argv.index("--") + 1:]
parser = argparse.ArgumentParser(description=__doc__, epilog=epilog)
# File path options
parser.add_argument(
"--image", dest="image", metavar='FILE',
help="Image file",
)
parser.add_argument(
"--output", dest="output", metavar='DIR',
help="Output directory",
)
parser.add_argument(
"--output_prefix", dest="output_prefix", metavar='STRING',
help="Output prefix",
)
# Icon naming option
parser.add_argument(
"--name_style", dest="name_style", metavar='ENUM', type=str,
choices=('', 'UI_ICONS'),
help="The method used for naming output data",
)
# Options for dicing up the image
parser.add_argument(
"--parts_x", dest="parts_x", metavar='INT', type=int,
help="Grid X parts",
)
parser.add_argument(
"--parts_y", dest="parts_y", metavar='INT', type=int,
help="Grid Y parts",
)
_help = "Inset from the outer edge (in pixels)"
parser.add_argument("--minx", dest="minx", metavar='INT', type=int, help=_help)
parser.add_argument("--miny", dest="miny", metavar='INT', type=int, help=_help)
parser.add_argument("--maxx", dest="maxx", metavar='INT', type=int, help=_help)
parser.add_argument("--maxy", dest="maxy", metavar='INT', type=int, help=_help)
_help = "Inset from each icons bounds (in pixels)"
parser.add_argument("--minx_icon", dest="minx_icon", metavar='INT', type=int, help=_help)
parser.add_argument("--miny_icon", dest="miny_icon", metavar='INT', type=int, help=_help)
parser.add_argument("--maxx_icon", dest="maxx_icon", metavar='INT', type=int, help=_help)
parser.add_argument("--maxy_icon", dest="maxy_icon", metavar='INT', type=int, help=_help)
_help = "Empty space between icons"
parser.add_argument("--spacex_icon", dest="spacex_icon", metavar='INT', type=int, help=_help)
parser.add_argument("--spacey_icon", dest="spacey_icon", metavar='INT', type=int, help=_help)
del _help
args = parser.parse_args(argv)
if not argv:
print("No args given!")
parser.print_help()
return
dice(args.image, args.output, args.output_prefix, args.name_style,
args.parts_x, args.parts_y,
args.minx, args.miny, args.maxx, args.maxy,
args.minx_icon, args.miny_icon, args.maxx_icon, args.maxy_icon,
args.spacex_icon, args.spacey_icon,
)
if __name__ == "__main__":
main()

@ -1,60 +0,0 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2014-2022 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# This script is just to view the icons
def write_png(buf, width, height):
import zlib
import struct
# reverse the vertical line order and add null bytes at the start
width_byte_4 = width * 4
raw_data = b"".join(
b'\x00' + buf[span:span + width_byte_4]
for span in range((height - 1) * width * 4, -1, - width_byte_4)
)
def png_pack(png_tag, data):
chunk_head = png_tag + data
return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))
return b"".join([
b'\x89PNG\r\n\x1a\n',
png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
png_pack(b'IDAT', zlib.compress(raw_data, 9)),
png_pack(b'IEND', b'')])
def icondata_to_png(file_src, file_dst):
import struct
with open(file_src, 'rb') as f_src:
# 2 ints
temp_data = f_src.read(4 * 2)
w, h = struct.unpack('<2I', temp_data)
temp_data = f_src.read(4 * 2) # (x, y) - ignored
temp_data = f_src.read(4 * 2) # (xfrom, yfrom) - ignored
# pixels
temp_data = f_src.read(w * h * 4)
buf = write_png(temp_data, w, h)
with open(file_dst, 'wb') as f_dst:
f_dst.write(buf)
def main():
import sys
import os
for arg in sys.argv[1:]:
file_src = arg
file_dst = os.path.splitext(arg)[0] + ".png"
icondata_to_png(file_src, file_dst)
if __name__ == "__main__":
main()