summaryrefslogtreecommitdiff
path: root/SmartDeviceLink/SDLArtwork.m
blob: d9bf2e586f2d8b79c23fd538ab59e4ae70e7f49b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//
//  SDLArtwork.m
//  SmartDeviceLink-iOS
//
//  Created by Joel Fischer on 10/15/15.
//  Copyright © 2015 smartdevicelink. All rights reserved.
//

#import "SDLArtwork.h"
#import "SDLFileType.h"
#import <CommonCrypto/CommonDigest.h>

NS_ASSUME_NONNULL_BEGIN

@interface SDLArtwork ()

@property (strong, nonatomic) UIImage *image;
@property (assign, nonatomic, readwrite) BOOL isTemplate;

@end


@implementation SDLArtwork

- (void)setImage:(UIImage *)image {
    _image = image;
    _isTemplate = (image.renderingMode == UIImageRenderingModeAlwaysTemplate);
}

#pragma mark - Lifecycle

+ (instancetype)artworkWithImage:(UIImage *)image name:(NSString *)name asImageFormat:(SDLArtworkImageFormat)imageFormat {
    return [[self alloc] initWithImage:image name:name persistent:NO asImageFormat:imageFormat];
}

+ (instancetype)artworkWithImage:(UIImage *)image asImageFormat:(SDLArtworkImageFormat)imageFormat {
    return [[self alloc] initWithImage:image persistent:NO asImageFormat:imageFormat];
}

+ (instancetype)persistentArtworkWithImage:(UIImage *)image name:(NSString *)name asImageFormat:(SDLArtworkImageFormat)imageFormat {
    return [[self alloc] initWithImage:image name:name persistent:YES asImageFormat:imageFormat];
}

+ (instancetype)persistentArtworkWithImage:(UIImage *)image asImageFormat:(SDLArtworkImageFormat)imageFormat {
    return [[self alloc] initWithImage:image persistent:YES asImageFormat:imageFormat];
}


#pragma mark Private Lifecycle

- (instancetype)initWithImage:(UIImage *)image name:(NSString *)name persistent:(BOOL)persistent asImageFormat:(SDLArtworkImageFormat)imageFormat {
    self.image = image;
    return [super initWithData:[self.class sdl_dataForUIImage:image imageFormat:imageFormat] name:name fileExtension:[self.class sdl_fileExtensionForImageFormat:imageFormat] persistent:persistent];
}

- (instancetype)initWithImage:(UIImage *)image persistent:(BOOL)persistent asImageFormat:(SDLArtworkImageFormat)imageFormat {
    self.image = image;
    NSData *imageData = [self.class sdl_dataForUIImage:image imageFormat:imageFormat];
    NSString *imageName = [self.class sdl_md5HashFromNSData:imageData];
    return [super initWithData:[self.class sdl_dataForUIImage:image imageFormat:imageFormat] name:(imageName != nil ? imageName : @"") fileExtension:[self.class sdl_fileExtensionForImageFormat:imageFormat] persistent:persistent];
}

/**
 * Returns the JPG or PNG image data for a UIImage.
 *
 *  @param image        A UIImage
 *  @param imageFormat  The image format to use when converting the UIImage to NSData
 *  @return             The image data
 */
+ (NSData *)sdl_dataForUIImage:(UIImage *)image imageFormat:(SDLArtworkImageFormat)imageFormat {
    NSData *imageData = nil;
    switch (imageFormat) {
        case SDLArtworkImageFormatPNG: {
            imageData = UIImagePNGRepresentation(image);
        } break;
        case SDLArtworkImageFormatJPG: {
            imageData = UIImageJPEGRepresentation(image, 0.85);
        } break;
    }
    return imageData;
}

/**
 *  Returns the file extension for the image format.
 *
 *  @param imageFormat  Whether the image is a PNG or JPG
 *  @return             The file extension for the image format
 */
+ (NSString *)sdl_fileExtensionForImageFormat:(SDLArtworkImageFormat)imageFormat {
    NSString *fileExtension = nil;
    switch (imageFormat) {
        case SDLArtworkImageFormatPNG: {
            fileExtension = @"png";
        } break;
        case SDLArtworkImageFormatJPG: {
            fileExtension = @"jpg";
        } break;
    }
    return fileExtension;
}

/**
 *  Creates a string representation of NSData by hashing the data using the MD5 hash function. This string is not guaranteed to be unique as collisions can occur, however collisions are extremely rare.
 *
 *  HAX: A MD5 hash always creates a string with 32 characters (128-bits). Due to some implementations of Core not following the spec, file names that are too long are being rejected. To try to accommodate this setup, hashed file names are being truncated to 16 characters.
 *
 *  Sourced from https://stackoverflow.com/questions/2018550/how-do-i-create-an-md5-hash-of-a-string-in-cocoa
 *
 *  @param data     The data to hash
 *  @return         A MD5 hash of the data
 */
+ (NSString *)sdl_md5HashFromNSData:(NSData *)data {
    if (data == nil) { return nil; }

    unsigned char hash[CC_MD5_DIGEST_LENGTH];
    CC_MD5([data bytes], (CC_LONG)[data length], hash);
    NSMutableString *formattedHash = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH];
    // HAX: To shorten the string to 16 characters, the loop has been shortened to 8 fom 16.
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH / 2; i += 1) {
        [formattedHash appendFormat:@"%02x", hash[i]];
    }
    return formattedHash;
}

#pragma mark - NSObject overrides

- (NSUInteger)hash {
    return self.name.hash ^ self.data.hash;
}

- (BOOL)isEqual:(id)object {
    if (self == object) { return YES; }

    if (![object isKindOfClass:[SDLArtwork class]]) { return NO; }

    return [self isEqualToArtwork:(SDLArtwork *)object];
}

- (BOOL)isEqualToArtwork:(SDLArtwork *)artwork {
    if (!artwork) { return NO; }

    BOOL haveEqualNames = [self.name isEqualToString:artwork.name];
    BOOL haveEqualData = [self.data isEqualToData:artwork.data];
    BOOL haveEqualFormats = [self.fileType isEqualToEnum:artwork.fileType];

    return haveEqualNames && haveEqualData && haveEqualFormats;
}

@end

NS_ASSUME_NONNULL_END