Browse Source
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
5 changed files with 422 additions and 0 deletions
Binary file not shown.
@ -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) |
||||||
@ -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; |
||||||
|
} |
||||||
@ -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…
Reference in new issue