Browse Source

Fix Tiger app icon

png2icns produces a 128x128 icon that isn't compatible with OSX Tiger.
Uses a patched version of png2icns to produce the correct icon.

Here is the output of icns2png -l AppIcon_128.icns.

Before:

```
 Extracting icons from AppIcon_128.icns...
 Icon family size is 22560 bytes (including 8 byte header)
 Listing icon elements...
  'is32' 16x16 32-bit icon (1024 bytes compressed to 710)
  's8mk' 16x16 8-bit mask (256 bytes)
  'il32' 32x32 32-bit icon (4096 bytes compressed to 2448)
  'l8mk' 32x32 8-bit mask (1024 bytes)
  'ih32' 48x48 32-bit icon (9216 bytes compressed to 4418)
  'h8mk' 48x48 8-bit mask (2304 bytes)
  'ic07' 128x128 32-bit icon (65536 bytes compressed to 11336)
```

After:

```
 Extracting icons from AppIcon_128.icns...
 Icon family size is 42420 bytes (including 8 byte header)
 Listing icon elements...
  'is32' 16x16 32-bit icon (1024 bytes compressed to 710)
  's8mk' 16x16 8-bit mask (256 bytes)
  'il32' 32x32 32-bit icon (4096 bytes compressed to 2300)
  'l8mk' 32x32 8-bit mask (1024 bytes)
  'it32' 128x128 32-bit icon (65536 bytes compressed to 21690)
  't8mk' 128x128 8-bit mask (16384 bytes)
```
pull/7806/head
Gleb Mazovetskiy 1 year ago
parent
commit
4f151c7e24
  1. BIN
      Packaging/apple/AppIcon_128.icns
  2. 1
      Packaging/apple/png2icns_tiger/.gitignore
  3. 8
      Packaging/apple/png2icns_tiger/CMakeLists.txt
  4. 354
      Packaging/apple/png2icns_tiger/png2icns_tiger.c
  5. 59
      Packaging/apple/png2icns_tiger/run.sh

BIN
Packaging/apple/AppIcon_128.icns

Binary file not shown.

1
Packaging/apple/png2icns_tiger/.gitignore vendored

@ -0,0 +1 @@
/build*/

8
Packaging/apple/png2icns_tiger/CMakeLists.txt

@ -0,0 +1,8 @@
# On Debian/Ubuntu, this requires libpng-dev and libicns-dev
cmake_minimum_required(VERSION 3.22)
project(png2icns_tiger LANGUAGES C)
add_executable(png2icns_tiger png2icns_tiger.c)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PNG REQUIRED IMPORTED_TARGET libpng)
pkg_check_modules(ICNS REQUIRED IMPORTED_TARGET libicns)
target_link_libraries(png2icns_tiger PRIVATE PkgConfig::PNG PkgConfig::ICNS)

354
Packaging/apple/png2icns_tiger/png2icns_tiger.c

@ -0,0 +1,354 @@
/*
* png2icns_tiger - based on png2icns from the pngutils Debian package but
* modified for Tiger support. See the section that says MODIFICATION.
*
* Copyright (C) 2008 Julien BLACHE <jb@jblache.org>
* Copyright (C) 2012 Mathew Eis <mathew@eisbox.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <icns.h>
#include <png.h>
#include <setjmp.h>
#define FALSE 0
#define TRUE 1
#if PNG_LIBPNG_VER >= 10209
#define PNG2ICNS_EXPAND_GRAY 1
#endif
static int read_png(FILE *fp, png_bytepp buffer, int32_t *bpp, int32_t *width,
int32_t *height) {
png_structp png_ptr;
png_infop info;
png_uint_32 w;
png_uint_32 h;
png_bytep *rows;
int bit_depth;
int32_t color_type;
int row;
int rowsize;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL)
return FALSE;
info = png_create_info_struct(png_ptr);
if (info == NULL) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
return FALSE;
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info, NULL);
return FALSE;
}
png_init_io(png_ptr, fp);
png_read_info(png_ptr, info);
png_get_IHDR(png_ptr, info, &w, &h, &bit_depth, &color_type, NULL, NULL,
NULL);
switch (color_type) {
case PNG_COLOR_TYPE_GRAY:
#ifdef PNG2ICNS_EXPAND_GRAY
png_set_expand_gray_1_2_4_to_8(png_ptr);
#else
png_set_gray_1_2_4_to_8(png_ptr);
#endif
if (bit_depth == 16) {
png_set_strip_16(png_ptr);
bit_depth = 8;
}
png_set_gray_to_rgb(png_ptr);
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
#ifdef PNG2ICNS_EXPAND_GRAY
png_set_expand_gray_1_2_4_to_8(png_ptr);
#else
png_set_gray_1_2_4_to_8(png_ptr);
#endif
if (bit_depth == 16) {
png_set_strip_16(png_ptr);
bit_depth = 8;
}
png_set_gray_to_rgb(png_ptr);
break;
case PNG_COLOR_TYPE_PALETTE:
png_set_palette_to_rgb(png_ptr);
if (png_get_valid(png_ptr, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png_ptr);
else
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
break;
case PNG_COLOR_TYPE_RGB:
if (bit_depth == 16) {
png_set_strip_16(png_ptr);
bit_depth = 8;
}
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
if (bit_depth == 16) {
png_set_strip_16(png_ptr);
bit_depth = 8;
}
break;
}
*width = w;
*height = h;
*bpp = bit_depth * 4;
png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info);
rowsize = png_get_rowbytes(png_ptr, info);
rows = malloc(sizeof(png_bytep) * h);
*buffer = malloc(rowsize * h + 8);
rows[0] = *buffer;
for (row = 1; row < h; row++) {
rows[row] = rows[row - 1] + rowsize;
}
png_read_image(png_ptr, rows);
png_destroy_read_struct(&png_ptr, &info, NULL);
free(rows);
return TRUE;
}
static int add_png_to_family(icns_family_t **iconFamily, char *pngname) {
FILE *pngfile;
int icnsErr = ICNS_STATUS_OK;
icns_image_t icnsImage;
icns_image_t icnsMask;
icns_type_t iconType;
icns_type_t maskType;
icns_icon_info_t iconInfo;
icns_element_t *iconElement = NULL;
icns_element_t *maskElement = NULL;
char iconStr[5] = {0, 0, 0, 0, 0};
char maskStr[5] = {0, 0, 0, 0, 0};
int iconDataOffset = 0;
int maskDataOffset = 0;
png_bytep buffer;
int width, height, bpp;
pngfile = fopen(pngname, "rb");
if (pngfile == NULL) {
fprintf(stderr, "Could not open '%s' for reading: %s\n", pngname,
strerror(errno));
return FALSE;
}
if (!read_png(pngfile, &buffer, &bpp, &width, &height)) {
fprintf(stderr, "Failed to read PNG file\n");
fclose(pngfile);
return FALSE;
}
fclose(pngfile);
icnsImage.imageWidth = width;
icnsImage.imageHeight = height;
icnsImage.imageChannels = 4;
icnsImage.imagePixelDepth = 8;
icnsImage.imageDataSize = width * height * 4;
icnsImage.imageData = buffer;
iconInfo.isImage = 1;
iconInfo.iconWidth = icnsImage.imageWidth;
iconInfo.iconHeight = icnsImage.imageHeight;
iconInfo.iconBitDepth = bpp;
iconInfo.iconChannels = (bpp == 32 ? 4 : 1);
iconInfo.iconPixelDepth = bpp / iconInfo.iconChannels;
iconType = icns_get_type_from_image_info(iconInfo);
maskType = icns_get_mask_type_for_icon_type(iconType);
/* MODIFICATION */
if (iconType == ICNS_128x128_32BIT_ARGB_DATA) {
/* libicns returns "ic07" for 128x128 icons but that doesn't work on Tiger
*/
iconType = ICNS_128X128_32BIT_DATA;
maskType = ICNS_128X128_8BIT_MASK;
}
/* END OF MODIFICATION */
icns_type_str(iconType, iconStr);
icns_type_str(maskType, maskStr);
/* Only convert the icons that match sizes icns supports */
if (iconType == ICNS_NULL_TYPE) {
fprintf(stderr, "Bad dimensions: PNG file '%s' is %dx%d\n", pngname, width,
height);
free(buffer);
return FALSE;
}
if (bpp != 32) {
fprintf(stderr, "Bit depth %d unsupported in '%s'\n", bpp, pngname);
free(buffer);
return FALSE;
}
icns_set_print_errors(0);
if (icns_get_element_from_family(*iconFamily, iconType, &iconElement) ==
ICNS_STATUS_OK) {
icns_set_print_errors(1);
fprintf(stderr, "Duplicate icon element of type '%s' detected (%s)\n",
iconStr, pngname);
free(buffer);
return FALSE;
}
icns_set_print_errors(1);
if ((iconType != ICNS_1024x1024_32BIT_ARGB_DATA) &&
(iconType != ICNS_512x512_32BIT_ARGB_DATA) &&
(iconType != ICNS_256x256_32BIT_ARGB_DATA) &&
(iconType != ICNS_128x128_32BIT_ARGB_DATA)) {
printf("Using icns type '%s', mask '%s' for '%s'\n", iconStr, maskStr,
pngname);
} else {
printf("Using icns type '%s' (ARGB) for '%s'\n", iconStr, pngname);
}
icnsErr = icns_new_element_from_image(&icnsImage, iconType, &iconElement);
if (iconElement != NULL) {
if (icnsErr == ICNS_STATUS_OK) {
icns_set_element_in_family(iconFamily, iconElement);
}
free(iconElement);
}
if ((iconType != ICNS_1024x1024_32BIT_ARGB_DATA) &&
(iconType != ICNS_512x512_32BIT_ARGB_DATA) &&
(iconType != ICNS_256x256_32BIT_ARGB_DATA) &&
(iconType != ICNS_128x128_32BIT_ARGB_DATA)) {
icns_init_image_for_type(maskType, &icnsMask);
iconDataOffset = 0;
maskDataOffset = 0;
while ((iconDataOffset < icnsImage.imageDataSize) &&
(maskDataOffset < icnsMask.imageDataSize)) {
icnsMask.imageData[maskDataOffset] =
icnsImage.imageData[iconDataOffset + 3];
iconDataOffset += 4; /* move to the next alpha byte */
maskDataOffset += 1; /* move to the next byte */
}
icnsErr = icns_new_element_from_mask(&icnsMask, maskType, &maskElement);
if (maskElement != NULL) {
if (icnsErr == ICNS_STATUS_OK) {
icns_set_element_in_family(iconFamily, maskElement);
}
free(maskElement);
}
icns_free_image(&icnsMask);
}
free(buffer);
return TRUE;
}
int main(int argc, char **argv) {
FILE *icnsfile;
icns_family_t *iconFamily;
int i;
if (argc < 3) {
printf("Usage: png2icns file.icns file1.png file2.png ... filen.png\n");
exit(1);
}
icnsfile = fopen(argv[1], "wb+");
if (icnsfile == NULL) {
fprintf(stderr, "Could not open '%s' for writing: %s\n", argv[1],
strerror(errno));
exit(1);
}
icns_set_print_errors(1);
icns_create_family(&iconFamily);
for (i = 2; i < argc; i++) {
if (!add_png_to_family(&iconFamily, argv[i])) {
fclose(icnsfile);
unlink(argv[1]);
exit(1);
}
}
if (icns_write_family_to_file(icnsfile, iconFamily) != ICNS_STATUS_OK) {
fprintf(stderr, "Failed to write icns file\n");
fclose(icnsfile);
exit(1);
}
fclose(icnsfile);
printf("Saved icns file to %s\n", argv[1]);
if (iconFamily != NULL)
free(iconFamily);
return 0;
}

59
Packaging/apple/png2icns_tiger/run.sh

@ -0,0 +1,59 @@
#!/bin/bash
set -euo pipefail
declare -r INPUT=../../resources/icon.png
declare -ra SIZES=(16 32 128)
declare -r OUTPUT=../AppIcon_128.icns
declare -rA DEPENDENCY_SOURCES=(
[convert]=imagemagick
[cmake]=cmake
)
check_deps() {
local -a missing_deps=()
local path
for dep in "${!DEPENDENCIES[@]}"; do
if path="$(which "$dep")"; then
echo >&2 "Using $dep from $path"
else
missing_deps+=("$dep")
fi
done
if (( ${#missing_deps[@]} )); then
echo >&2 "Error: Missing dependencies"
for dep in "${missing_deps[@]}"; do
echo >&2 '* '"Please install \"${dep}\", provided by ${DEPENDENCY_SOURCES[$dep]} on Debian/Ubuntu"
done
exit 1
fi
}
main() {
cd "$(dirname "$0")"
check_deps
set -x
mkdir -p tmp
{ set +x; } 2> /dev/null
local path
local -a FILES=()
for s in "${SIZES[@]}"; do
path="tmp/output_${s}.png"
FILES+=("$path")
set -x
convert "$INPUT" -resize "${s}x${s}" "$path"
{ set +x; } 2> /dev/null
done
set -x
cmake -S. -Bbuild-rel -DCMAKE_BUILD_TYPE=Release
cmake --build build-rel
build-rel/png2icns_tiger "$OUTPUT" "${FILES[@]}"
rm -rf tmp
}
main "$@"
Loading…
Cancel
Save