Skip to content

Commit

Permalink
Fix Tiger app icon
Browse files Browse the repository at this point in the history
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)
```
  • Loading branch information
glebm committed Mar 2, 2025
1 parent 08b4a81 commit 4f151c7
Show file tree
Hide file tree
Showing 5 changed files with 422 additions and 0 deletions.
Binary file modified Packaging/apple/AppIcon_128.icns
Binary file not shown.
1 change: 1 addition & 0 deletions Packaging/apple/png2icns_tiger/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build*/
8 changes: 8 additions & 0 deletions Packaging/apple/png2icns_tiger/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 changes: 354 additions & 0 deletions Packaging/apple/png2icns_tiger/png2icns_tiger.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
* Copyright (C) 2012 Mathew Eis <[email protected]>
*
* 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;
}
Loading

0 comments on commit 4f151c7

Please sign in to comment.