| 1 | #include "fitz-imp.h" |
| 2 | |
| 3 | #include <string.h> |
| 4 | #include <limits.h> |
| 5 | |
| 6 | struct info |
| 7 | { |
| 8 | int gif89a; |
| 9 | unsigned int width, height; |
| 10 | unsigned char aspect; |
| 11 | unsigned int xres, yres; |
| 12 | |
| 13 | unsigned int image_left, image_top; |
| 14 | unsigned int image_width, image_height; |
| 15 | unsigned int image_interlaced; |
| 16 | |
| 17 | int has_gct; |
| 18 | int gct_entries; |
| 19 | unsigned char *gct; |
| 20 | unsigned int gct_background; |
| 21 | |
| 22 | int has_lct; |
| 23 | int lct_entries; |
| 24 | unsigned char *lct; |
| 25 | |
| 26 | int has_transparency; |
| 27 | unsigned int transparent; |
| 28 | unsigned char *mask; |
| 29 | |
| 30 | fz_pixmap *pix; |
| 31 | }; |
| 32 | |
| 33 | /* default color table, where the first two entries are black and white */ |
| 34 | static const unsigned char dct[256 * 3] = { |
| 35 | 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, |
| 36 | 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, |
| 37 | 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, |
| 38 | 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, |
| 39 | 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, |
| 40 | 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, |
| 41 | 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, |
| 42 | 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, |
| 43 | 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, |
| 44 | 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, |
| 45 | 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, |
| 46 | 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, |
| 47 | 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, |
| 48 | 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, |
| 49 | 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, |
| 50 | 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, |
| 51 | 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, |
| 52 | 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, |
| 53 | 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, |
| 54 | 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, |
| 55 | 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, |
| 56 | 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, |
| 57 | 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, |
| 58 | 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, |
| 59 | 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, |
| 60 | 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, |
| 61 | 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, |
| 62 | 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, |
| 63 | 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, |
| 64 | 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, |
| 65 | 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, |
| 66 | 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, |
| 67 | 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, |
| 68 | 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, |
| 69 | 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, |
| 70 | 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, |
| 71 | 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, |
| 72 | 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, |
| 73 | 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, |
| 74 | 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, |
| 75 | 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, |
| 76 | 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, |
| 77 | 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, |
| 78 | 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, |
| 79 | 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, |
| 80 | 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, |
| 81 | 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, |
| 82 | 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, |
| 83 | 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, |
| 84 | 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, |
| 85 | 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, |
| 86 | 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, |
| 87 | 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, |
| 88 | 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, |
| 89 | 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, |
| 90 | 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, |
| 91 | 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, |
| 92 | 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, |
| 93 | 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, |
| 94 | 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, |
| 95 | 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, |
| 96 | 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, |
| 97 | 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, |
| 98 | 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, |
| 99 | }; |
| 100 | |
| 101 | static const unsigned char * |
| 102 | gif_read_subblocks(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end, fz_buffer *buf) |
| 103 | { |
| 104 | int len; |
| 105 | |
| 106 | do |
| 107 | { |
| 108 | if (end - p < 1) |
| 109 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in data subblocks in gif image" ); |
| 110 | len = *p; |
| 111 | p += 1; |
| 112 | |
| 113 | if (len > 0) |
| 114 | { |
| 115 | if (end - p < len) |
| 116 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in data subblock in gif image" ); |
| 117 | if (buf) |
| 118 | fz_append_data(ctx, buf, p, len); |
| 119 | p += len; |
| 120 | } |
| 121 | } while (len > 0); |
| 122 | |
| 123 | return p; |
| 124 | } |
| 125 | |
| 126 | static const unsigned char * |
| 127 | (fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 128 | { |
| 129 | if (end - p < 6) |
| 130 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in header in gif image" ); |
| 131 | |
| 132 | if (memcmp(&p[0], "GIF" , 3)) |
| 133 | fz_throw(ctx, FZ_ERROR_GENERIC, "invalid signature in gif image" ); |
| 134 | if (memcmp(&p[3], "87a" , 3) && memcmp(&p[3], "89a" , 3)) |
| 135 | fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported version in gif image" ); |
| 136 | |
| 137 | info->gif89a = !memcmp(p, "GIF89a" , 6); |
| 138 | |
| 139 | return p + 6; |
| 140 | } |
| 141 | |
| 142 | static const unsigned char * |
| 143 | gif_read_lsd(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 144 | { |
| 145 | if (end - p < 7) |
| 146 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in logical screen descriptor in gif image" ); |
| 147 | |
| 148 | info->width = p[1] << 8 | p[0]; |
| 149 | info->height = p[3] << 8 | p[2]; |
| 150 | if (info->width <= 0) |
| 151 | fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0" ); |
| 152 | if (info->height <= 0) |
| 153 | fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0" ); |
| 154 | if (info->height > UINT_MAX / info->width / 3 /* components */) |
| 155 | fz_throw(ctx, FZ_ERROR_GENERIC, "image dimensions might overflow" ); |
| 156 | |
| 157 | info->has_gct = (p[4] >> 7) & 0x1; |
| 158 | if (info->has_gct) |
| 159 | { |
| 160 | info->gct_entries = 1 << ((p[4] & 0x7) + 1); |
| 161 | info->gct_background = fz_clampi(p[5], 0, info->gct_entries - 1); |
| 162 | } |
| 163 | info->aspect = p[6]; |
| 164 | |
| 165 | info->xres = 96; |
| 166 | info->yres= 96; |
| 167 | if (info->aspect > 0) |
| 168 | info->yres = (((float) info->aspect + 15) / 64) * 96; |
| 169 | |
| 170 | return p + 7; |
| 171 | } |
| 172 | |
| 173 | static const unsigned char * |
| 174 | gif_read_gct(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 175 | { |
| 176 | if (end - p < info->gct_entries * 3) |
| 177 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in global color table in gif image" ); |
| 178 | |
| 179 | info->gct = fz_malloc(ctx, info->gct_entries * 3); |
| 180 | memmove(info->gct, p, info->gct_entries * 3); |
| 181 | |
| 182 | return p + info->gct_entries * 3; |
| 183 | } |
| 184 | |
| 185 | static const unsigned char * |
| 186 | gif_read_id(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 187 | { |
| 188 | if (end - p < 10) |
| 189 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in image descriptor in gif image" ); |
| 190 | |
| 191 | info->image_left = p[2] << 8 | p[1]; |
| 192 | info->image_top = p[4] << 8 | p[3]; |
| 193 | info->image_width = p[6] << 8 | p[5]; |
| 194 | info->image_height = p[8] << 8 | p[7]; |
| 195 | info->has_lct = (p[9] >> 7) & 0x1; |
| 196 | info->image_interlaced = (p[9] >> 6) & 0x1; |
| 197 | |
| 198 | if (info->has_lct) |
| 199 | info->lct_entries = 1 << ((p[9] & 0x7) + 1); |
| 200 | |
| 201 | return p + 10; |
| 202 | } |
| 203 | |
| 204 | static const unsigned char * |
| 205 | gif_read_lct(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 206 | { |
| 207 | if (end - p < info->lct_entries * 3) |
| 208 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in local color table in gif image" ); |
| 209 | |
| 210 | info->lct = fz_malloc(ctx, info->lct_entries * 3); |
| 211 | memmove(info->lct, p, info->lct_entries * 3); |
| 212 | |
| 213 | return p + info->lct_entries * 3; |
| 214 | } |
| 215 | |
| 216 | static void |
| 217 | gif_read_line(fz_context *ctx, struct info *info, int ct_entries, const unsigned char *ct, unsigned int y, unsigned char *sp) |
| 218 | { |
| 219 | unsigned int index = (info->image_top + y) * info->width + info->image_left; |
| 220 | unsigned char *samples = fz_pixmap_samples(ctx, info->pix); |
| 221 | unsigned char *dp = &samples[index * 4]; |
| 222 | unsigned char *mp = &info->mask[index]; |
| 223 | unsigned int x, k; |
| 224 | |
| 225 | if (info->image_top + y >= info->height) |
| 226 | return; |
| 227 | |
| 228 | for (x = 0; x < info->image_width && info->image_left + x < info->width; x++, sp++, mp++, dp += 4) |
| 229 | if (!info->has_transparency || *sp != info->transparent) |
| 230 | { |
| 231 | *mp = 0x02; |
| 232 | for (k = 0; k < 3; k++) |
| 233 | dp[k] = ct[fz_clampi(*sp, 0, ct_entries - 1) * 3 + k]; |
| 234 | dp[3] = 255; |
| 235 | } |
| 236 | else if (*mp == 0x01) |
| 237 | *mp = 0x00; |
| 238 | } |
| 239 | |
| 240 | static const unsigned char * |
| 241 | gif_read_tbid(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 242 | { |
| 243 | fz_stream *stm = NULL, *lzwstm = NULL; |
| 244 | unsigned int mincodesize, y; |
| 245 | fz_buffer *compressed = NULL, *uncompressed = NULL; |
| 246 | const unsigned char *ct; |
| 247 | unsigned char *sp; |
| 248 | int ct_entries; |
| 249 | |
| 250 | if (end - p < 1) |
| 251 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in table based image data in gif image" ); |
| 252 | |
| 253 | mincodesize = *p; |
| 254 | |
| 255 | /* if there is no overlap, avoid pasting image data, just consume it */ |
| 256 | if (info->image_top >= info->height || info->image_left >= info->width) |
| 257 | { |
| 258 | p = gif_read_subblocks(ctx, info, p + 1, end, NULL); |
| 259 | return p; |
| 260 | } |
| 261 | |
| 262 | fz_var(compressed); |
| 263 | fz_var(lzwstm); |
| 264 | fz_var(stm); |
| 265 | fz_var(uncompressed); |
| 266 | |
| 267 | fz_try(ctx) |
| 268 | { |
| 269 | compressed = fz_new_buffer(ctx, 0); |
| 270 | p = gif_read_subblocks(ctx, info, p + 1, end, compressed); |
| 271 | |
| 272 | stm = fz_open_buffer(ctx, compressed); |
| 273 | lzwstm = fz_open_lzwd(ctx, stm, 0, mincodesize + 1, 1, 1); |
| 274 | |
| 275 | uncompressed = fz_read_all(ctx, lzwstm, 0); |
| 276 | if (uncompressed->len < info->image_width * info->image_height) |
| 277 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in compressed table based image data in gif image" ); |
| 278 | |
| 279 | if (info->has_lct) |
| 280 | { |
| 281 | ct = info->lct; |
| 282 | ct_entries = info->lct_entries; |
| 283 | } |
| 284 | else if (info->has_gct) |
| 285 | { |
| 286 | ct = info->gct; |
| 287 | ct_entries = info->gct_entries; |
| 288 | } |
| 289 | else |
| 290 | { |
| 291 | ct = dct; |
| 292 | ct_entries = 256; |
| 293 | } |
| 294 | |
| 295 | sp = uncompressed->data; |
| 296 | if (info->image_interlaced) |
| 297 | { |
| 298 | for (y = 0; y < info->image_height; y += 8, sp += info->image_width) |
| 299 | gif_read_line(ctx, info, ct_entries, ct, y, sp); |
| 300 | for (y = 4; y < info->image_height; y += 8, sp += info->image_width) |
| 301 | gif_read_line(ctx, info, ct_entries, ct, y, sp); |
| 302 | for (y = 2; y < info->image_height; y += 4, sp += info->image_width) |
| 303 | gif_read_line(ctx, info, ct_entries, ct, y, sp); |
| 304 | for (y = 1; y < info->image_height; y += 2, sp += info->image_width) |
| 305 | gif_read_line(ctx, info, ct_entries, ct, y, sp); |
| 306 | } |
| 307 | else |
| 308 | for (y = 0; y < info->image_height; y++, sp += info->image_width) |
| 309 | gif_read_line(ctx, info, ct_entries, ct, y, sp); |
| 310 | } |
| 311 | fz_always(ctx) |
| 312 | { |
| 313 | fz_drop_buffer(ctx, uncompressed); |
| 314 | fz_drop_buffer(ctx, compressed); |
| 315 | fz_drop_stream(ctx, lzwstm); |
| 316 | fz_drop_stream(ctx, stm); |
| 317 | } |
| 318 | fz_catch(ctx) |
| 319 | { |
| 320 | fz_rethrow(ctx); |
| 321 | } |
| 322 | |
| 323 | return p; |
| 324 | } |
| 325 | |
| 326 | static const unsigned char * |
| 327 | gif_read_gce(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 328 | { |
| 329 | if (end - p < 8) |
| 330 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in graphic control extension in gif image" ); |
| 331 | if (p[2] != 0x04) |
| 332 | fz_throw(ctx, FZ_ERROR_GENERIC, "out of range graphic control extension block size in gif image" ); |
| 333 | |
| 334 | info->has_transparency = p[3] & 0x1; |
| 335 | if (info->has_transparency) |
| 336 | info->transparent = p[6]; |
| 337 | |
| 338 | return p + 8; |
| 339 | } |
| 340 | |
| 341 | static const unsigned char * |
| 342 | gif_read_ce(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 343 | { |
| 344 | return gif_read_subblocks(ctx, info, p + 2, end, NULL); |
| 345 | } |
| 346 | |
| 347 | static const unsigned char* |
| 348 | gif_read_pte(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 349 | { |
| 350 | if (end - p < 15) |
| 351 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in plain text extension in gif image" ); |
| 352 | if (p[2] != 0x0c) |
| 353 | fz_throw(ctx, FZ_ERROR_GENERIC, "out of range plain text extension block size in gif image" ); |
| 354 | return gif_read_subblocks(ctx, info, p + 15, end, NULL); |
| 355 | } |
| 356 | |
| 357 | static const unsigned char * |
| 358 | gif_read_icc(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 359 | { |
| 360 | #if FZ_ENABLE_ICC |
| 361 | fz_colorspace *icc = NULL; |
| 362 | fz_buffer *buf = NULL; |
| 363 | |
| 364 | fz_var(p); |
| 365 | |
| 366 | buf = fz_new_buffer(ctx, 0); |
| 367 | fz_try(ctx) |
| 368 | { |
| 369 | p = gif_read_subblocks(ctx, info, p, end, buf); |
| 370 | icc = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, 0, NULL, buf); |
| 371 | fz_drop_colorspace(ctx, info->pix->colorspace); |
| 372 | info->pix->colorspace = icc; |
| 373 | } |
| 374 | fz_always(ctx) |
| 375 | fz_drop_buffer(ctx, buf); |
| 376 | fz_catch(ctx) |
| 377 | fz_warn(ctx, "ignoring embedded ICC profile in GIF" ); |
| 378 | |
| 379 | return p; |
| 380 | #else |
| 381 | return gif_read_subblocks(ctx, info, p, end, NULL); |
| 382 | #endif |
| 383 | } |
| 384 | |
| 385 | /* |
| 386 | NETSCAPE2.0 |
| 387 | http://odur.let.rug.nl/~kleiweg/gif/netscape.html |
| 388 | http://www.vurdalakov.net/misc/gif/netscape-looping-application-extension |
| 389 | http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension |
| 390 | https://code.google.com/p/gifdotnet/source/browse/src/GifDotNet/GifApplicationExtensionBlock.cs#95 |
| 391 | http://trac.webkit.org/browser/trunk/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp#L617 |
| 392 | |
| 393 | ANIMEXTS1.0 |
| 394 | http://www.vurdalakov.net/misc/gif/animexts-looping-application-extension |
| 395 | https://code.google.com/p/gifdotnet/source/browse/src/GifDotNet/GifApplicationExtensionBlock.cs#95 |
| 396 | |
| 397 | ICCRGBG1012 |
| 398 | http://www.color.org/icc1V42.pdf |
| 399 | |
| 400 | XMP DataXMP |
| 401 | http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf |
| 402 | |
| 403 | fractint |
| 404 | http://fractint.net/fractsvn/trunk/fractint/common/loadfile.c |
| 405 | |
| 406 | ZGATEXTI5 ZGATILEI5 ZGACTRLI5 ZGANPIMGI5 |
| 407 | ZGAVECTI5 ZGAALPHAI5 ZGATITLE4.0 ZGATEXTI4.0 |
| 408 | Zoner GIF animator 4.0 and 5.0 |
| 409 | */ |
| 410 | static const unsigned char * |
| 411 | gif_read_ae(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end) |
| 412 | { |
| 413 | static char *ignorable[] = { |
| 414 | "NETSACPE2.0" , "ANIMEXTS1.0" , "XMP DataXMP" , |
| 415 | "ZGATEXTI5\0\0" , "ZGATILEI5\0\0" , "ZGANPIMGI5\0" , "ZGACTRLI5\0\0" , |
| 416 | "ZGAVECTI5\0\0" , "ZGAALPHAI5\0" , "ZGATITLE4.0" , "ZGATEXTI4.0" , |
| 417 | }; |
| 418 | int i, ignored; |
| 419 | |
| 420 | if (end - p < 14) |
| 421 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in application extension in gif image" ); |
| 422 | if (p[2] != 0x0b) |
| 423 | fz_throw(ctx, FZ_ERROR_GENERIC, "out of range application extension block size in gif image" ); |
| 424 | |
| 425 | ignored = 0; |
| 426 | for (i = 0; i < nelem(ignorable); i++) |
| 427 | ignored |= memcmp(&p[3], ignorable[i], 8 + 3); |
| 428 | if (!ignored) |
| 429 | { |
| 430 | char extension[9]; |
| 431 | memmove(extension, &p[3], 8); |
| 432 | extension[8] = '\0'; |
| 433 | fz_warn(ctx, "ignoring unsupported application extension '%s' in gif image" , extension); |
| 434 | } |
| 435 | |
| 436 | if (!memcmp(&p[3], "ICCRGBG1012" , 11)) |
| 437 | return gif_read_icc(ctx, info, p + 14, end); |
| 438 | |
| 439 | return gif_read_subblocks(ctx, info, p + 14, end, NULL); |
| 440 | } |
| 441 | |
| 442 | static void |
| 443 | gif_mask_transparency(fz_context *ctx, struct info *info) |
| 444 | { |
| 445 | unsigned char *mp = info->mask; |
| 446 | unsigned char *dp = fz_pixmap_samples(ctx, info->pix); |
| 447 | unsigned int x, y; |
| 448 | |
| 449 | for (y = 0; y < info->height; y++) |
| 450 | for (x = 0; x < info->width; x++, mp++, dp += 4) |
| 451 | if (*mp == 0x00) |
| 452 | dp[3] = 0; |
| 453 | } |
| 454 | |
| 455 | static fz_pixmap * |
| 456 | gif_read_image(fz_context *ctx, struct info *info, const unsigned char *p, size_t total, int only_metadata) |
| 457 | { |
| 458 | const unsigned char *end = p + total; |
| 459 | |
| 460 | memset(info, 0x00, sizeof (*info)); |
| 461 | |
| 462 | /* Read header */ |
| 463 | p = gif_read_header(ctx, info, p, end); |
| 464 | |
| 465 | /* Read logical screen descriptor */ |
| 466 | p = gif_read_lsd(ctx, info, p, end); |
| 467 | |
| 468 | if (only_metadata) |
| 469 | return NULL; |
| 470 | |
| 471 | info->pix = fz_new_pixmap(ctx, fz_device_rgb(ctx), info->width, info->height, NULL, 1); |
| 472 | |
| 473 | fz_try(ctx) |
| 474 | { |
| 475 | info->mask = fz_calloc(ctx, info->width * info->height, 1); |
| 476 | |
| 477 | /* Read optional global color table */ |
| 478 | if (info->has_gct) |
| 479 | { |
| 480 | unsigned char *bp, *dp = fz_pixmap_samples(ctx, info->pix); |
| 481 | unsigned int x, y, k; |
| 482 | |
| 483 | p = gif_read_gct(ctx, info, p, end); |
| 484 | bp = &info->gct[info->gct_background * 3]; |
| 485 | |
| 486 | memset(info->mask, 0x01, info->width * info->height); |
| 487 | |
| 488 | for (y = 0; y < info->height; y++) |
| 489 | for (x = 0; x < info->width; x++, dp += 4) |
| 490 | { |
| 491 | for (k = 0; k < 3; k++) |
| 492 | dp[k] = bp[k]; |
| 493 | dp[3] = 255; |
| 494 | } |
| 495 | } |
| 496 | |
| 497 | while (1) |
| 498 | { |
| 499 | /* Read block indicator */ |
| 500 | if (end - p < 1) |
| 501 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of block indicator in gif image" ); |
| 502 | |
| 503 | /* Read trailer */ |
| 504 | if (p[0] == 0x3b) |
| 505 | { |
| 506 | break; |
| 507 | } |
| 508 | /* Read extension */ |
| 509 | else if (p[0] == 0x21) |
| 510 | { |
| 511 | /* Read extension label */ |
| 512 | if (end - p < 2) |
| 513 | fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in extension label in gif image" ); |
| 514 | |
| 515 | if (p[1] == 0x01 && info->gif89a) |
| 516 | { |
| 517 | /* Read plain text extension */ |
| 518 | p = gif_read_pte(ctx, info, p, end); |
| 519 | |
| 520 | /* Graphic control extension applies only to the graphic rendering block following it */ |
| 521 | info->transparent = 0; |
| 522 | info->has_transparency = 0; |
| 523 | } |
| 524 | else if (p[1] == 0xf9 && info->gif89a) |
| 525 | /* Read graphic control extension */ |
| 526 | p = gif_read_gce(ctx, info, p, end); |
| 527 | else if (p[1] == 0xfe && info->gif89a) |
| 528 | /* Read comment extension */ |
| 529 | p = gif_read_ce(ctx, info, p, end); |
| 530 | else if (p[1] == 0xff && info->gif89a) |
| 531 | /* Read application extension */ |
| 532 | p = gif_read_ae(ctx, info, p, end); |
| 533 | else |
| 534 | fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported extension label %02x in gif image" , p[1]); |
| 535 | } |
| 536 | /* Read image descriptor */ |
| 537 | else if (p[0] == 0x2c) |
| 538 | { |
| 539 | p = gif_read_id(ctx, info, p, end); |
| 540 | |
| 541 | if (info->has_lct) |
| 542 | /* Read local color table */ |
| 543 | p = gif_read_lct(ctx, info, p, end); |
| 544 | |
| 545 | /* Read table based image data */ |
| 546 | p = gif_read_tbid(ctx, info, p, end); |
| 547 | |
| 548 | /* Graphic control extension applies only to the graphic rendering block following it */ |
| 549 | info->transparent = 0; |
| 550 | info->has_transparency = 0; |
| 551 | |
| 552 | /* Image descriptor applies only to the table based image data following it */ |
| 553 | info->image_left = info->image_top = 0; |
| 554 | info->image_width = info->width; |
| 555 | info->image_height = info->height; |
| 556 | info->image_interlaced = 0; |
| 557 | fz_free(ctx, info->lct); |
| 558 | info->lct = NULL; |
| 559 | info->has_lct = 0; |
| 560 | } |
| 561 | else |
| 562 | fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported block indicator %02x in gif image" , p[0]); |
| 563 | } |
| 564 | |
| 565 | gif_mask_transparency(ctx, info); |
| 566 | fz_premultiply_pixmap(ctx, info->pix); |
| 567 | } |
| 568 | fz_always(ctx) |
| 569 | { |
| 570 | fz_free(ctx, info->mask); |
| 571 | fz_free(ctx, info->lct); |
| 572 | fz_free(ctx, info->gct); |
| 573 | } |
| 574 | fz_catch(ctx) |
| 575 | { |
| 576 | fz_drop_pixmap(ctx, info->pix); |
| 577 | fz_rethrow(ctx); |
| 578 | } |
| 579 | |
| 580 | return info->pix; |
| 581 | } |
| 582 | |
| 583 | fz_pixmap * |
| 584 | fz_load_gif(fz_context *ctx, const unsigned char *p, size_t total) |
| 585 | { |
| 586 | fz_pixmap *image; |
| 587 | struct info gif; |
| 588 | |
| 589 | image = gif_read_image(ctx, &gif, p, total, 0); |
| 590 | image->xres = gif.xres; |
| 591 | image->yres = gif.yres; |
| 592 | |
| 593 | return image; |
| 594 | } |
| 595 | |
| 596 | void |
| 597 | fz_load_gif_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) |
| 598 | { |
| 599 | struct info gif; |
| 600 | |
| 601 | gif_read_image(ctx, &gif, p, total, 1); |
| 602 | *cspacep = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); |
| 603 | *wp = gif.width; |
| 604 | *hp = gif.height; |
| 605 | *xresp = gif.xres; |
| 606 | *yresp = gif.yres; |
| 607 | } |
| 608 | |