CloverBootloader/CloverApp/Clover/ThemeImage.m

173 lines
6.4 KiB
Objective-C

//
// PNG8Image.m
// Clover
//
// Created by vector sigma on 07/03/2020.
// Copyright © 2020 CloverHackyColor. All rights reserved.
//
#import "ThemeImage.h"
@implementation ThemeImage
- (id _Nullable)initWithData:(nonnull NSData *)data
error:(NSError *_Nullable*_Nullable)errorPtr
atPath:(nonnull NSString *)path {
if (!(self = [super init])) {
return nil;
}
NSData *mainData = data;
NSString *domain = @"org.slice.Clover.PNG8Image.Error";
if (!mainData || [mainData length] < 4) {
NSString *desc = [NSString stringWithFormat:@"Size of %@ is too small to be an image\n", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:1
userInfo:userInfo];
return nil;
}
UInt8 * bytes = (UInt8 *)[mainData bytes];
if (bytes[0] != 0x89 || bytes[0] != 0x50 || bytes[0] != 0x4E || bytes[0] != 0x47) {
NSBitmapImageRep *bir = [[NSBitmapImageRep alloc] initWithData:mainData];
if (bir) {
mainData = [bir representationUsingType:NSPNGFileType properties:@{ NSImageInterlaced: @0,
NSImageCompressionFactor: @1 }];
if (mainData == nil) {
NSString *desc = [NSString stringWithFormat:@"Can't convert %@ to png\n", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:2
userInfo:userInfo];
return nil;
} else {
[self addRepresentation: [[NSBitmapImageRep alloc] initWithData:mainData]];
}
}
}
unsigned int width, height;
unsigned char *raw_rgba_pixels;
unsigned int status = lodepng_decode32(&raw_rgba_pixels,
&width,
&height,
[mainData bytes],
[mainData length]);
if (status) {
NSString *desc = [NSString stringWithFormat:@"%@, %s\n",
path,
lodepng_error_text(status)];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:3
userInfo:userInfo];
return nil;
}
// Use libimagequant to make a palette for the RGBA pixels
liq_attr *handle = liq_attr_create();
liq_image *input_image = liq_image_create_rgba(handle,
raw_rgba_pixels,
width,
height,
0);
// You could set more options here, like liq_set_quality
liq_result *quantization_result;
if (liq_image_quantize(input_image, handle, &quantization_result) != LIQ_OK) {
NSString *desc = [NSString stringWithFormat:@"Quantization failed for %@", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:4
userInfo:userInfo];
return nil;
}
// Use libimagequant to make new image pixels from the palette
size_t pixels_size = width * height;
unsigned char *raw_8bit_pixels = malloc(pixels_size);
liq_set_dithering_level(quantization_result, 1.0);
liq_write_remapped_image(quantization_result,
input_image,
raw_8bit_pixels,
pixels_size);
const liq_palette *palette = liq_get_palette(quantization_result);
// Save converted pixels as a PNG file
// This uses lodepng library for PNG writing (not part of libimagequant)
LodePNGState state;
lodepng_state_init(&state);
state.info_raw.colortype = LCT_PALETTE;
state.info_raw.bitdepth = 8;
state.info_png.color.colortype = LCT_PALETTE;
state.info_png.color.bitdepth = 8;
for(int i = 0; i < palette->count; i++) {
lodepng_palette_add(&state.info_png.color,
palette->entries[i].r,
palette->entries[i].g,
palette->entries[i].b,
palette->entries[i].a);
lodepng_palette_add(&state.info_raw,
palette->entries[i].r,
palette->entries[i].g,
palette->entries[i].b,
palette->entries[i].a);
}
unsigned char *output_file_data;
size_t output_file_size;
unsigned int out_status = lodepng_encode(&output_file_data,
&output_file_size,
raw_8bit_pixels,
width,
height,
&state);
if (out_status) {
NSString *desc = [NSString stringWithFormat:@"Can't encode %@: %s\n",
path,
lodepng_error_text(out_status)];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:5
userInfo:userInfo];
return nil;
}
// Prove the conversion
self.pngData = [NSData dataWithBytes: output_file_data length: output_file_size];
NSImage *convertedImage = [[NSImage alloc] initWithData:self.pngData];
if (convertedImage == nil) {
NSString *desc = [NSString stringWithFormat:@"Can't convert data to NSImage (%@)", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:6
userInfo:userInfo];
liq_result_destroy(quantization_result); // Must be freed only after you're done using the palette
liq_image_destroy(input_image);
liq_attr_destroy(handle);
free(raw_8bit_pixels);
lodepng_state_cleanup(&state);
return nil;
}
liq_result_destroy(quantization_result); // Must be freed only after you're done using the palette
liq_image_destroy(input_image);
liq_attr_destroy(handle);
free(raw_8bit_pixels);
lodepng_state_cleanup(&state);
return self;
}
@end