├── Makefile ├── README └── dump2png.c /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -O3 -lpng -lm -o dump2png dump2png.c 3 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Visualize file data as a png. Intended for memory or core dumps. 2 | 3 | This tool is an experiment, intended to characterize the memory usage of large 4 | process core dumps. It does by converting each byte to a colored pixel, and 5 | building an image from these line by line. For serious core dump analysis, 6 | look for other tools that read the metadata and structure from the dump. 7 | 8 | By default, the least significant bit is masked, so that the image can't be 9 | converted back to the input file, to avoid inadvertent privacy leaks. Use -M 10 | to avoid masking, or increase BYTE_MASK to mask more bits. 11 | 12 | 1. Build 13 | 14 | Using gcc: gcc -O3 -lm -lpng -o dump2png dump2png.c 15 | 16 | Requires libpng. This is a good candidate for optimization (-O3). 17 | 18 | 2. Usage 19 | 20 | $ ./dump2png --help 21 | USAGE: dump2png [-HM] [-w width] [-h height_max] 22 | [-p palette] [-o outfile.png] 23 | [-k skip_factor] [-s seek_bytes] 24 | [-z zoom_factor] file 25 | 26 | [--help] # for full help 27 | 28 | palette types: gray, gray16b, gray16l, gray32b, gray32l, 29 | hues, hues6, fhues, color, color16, color32, rgb, 30 | dvi, x86 (default). 31 | 32 | -H don't autoscale height 33 | -M don't mask least significant bit 34 | -k skip_factor skips horiz lines; eg, 3 means show 1 out of 3 35 | -s seek_bytes the byte offset of the infile to begin reading 36 | -z zoom_factor averages multiple bytes; eg, 16 avgs 16 as 1 37 | -z palette palette type for colorization: 38 | 39 | gray grayscale, per byte 40 | gray16b grayscale, per short (big-endian) 41 | gray16l grayscale, per short (little-endian) 42 | gray32b grayscale, per long (big-endian) 43 | gray32l grayscale, per long (little-endian) 44 | hues map to 3 hue ranges (rgb), per byte (zoom safe) 45 | hues6 map to 6 hue ranges (rgbcmy), per byte 46 | fhues map to 3 full hue ranges (rgb), per byte (zoom safe) 47 | color full colorized scale, per byte 48 | color16 full colorized scale, per short (16-bit) 49 | color32 full colorized scale, per long (32-bit) 50 | rgb treat 3 sequential bytes as RGB 51 | dvi use RGB to convey differential, value, integral 52 | x86 grayscale with some (9) color indicators: 53 | 54 | green = common english chars: 'e', 't', 'a' 55 | red = common x86 instructions: movl, call, testl 56 | blue = binary values: 0x01, 0x02, 0x03 57 | 58 | 3. Examples 59 | 60 | $ ./dump2png core.node.13562 # by default uses "x86" palette 61 | $ ./dump2png -w 2048 core # output image 2048 pixels wide 62 | $ ./dump2png -o out.png core # write to "out.png" 63 | $ ./dump2png -p gray core # grayscale palette 64 | $ ./dump2png -p color core # full color palette 65 | $ ./dump2png -p hues core # RGB hues only (zoom friendly) 66 | $ ./dump2png -z 32 core # Zoom out by 32x (32 bytes averaged as 1 pixel) 67 | $ ./dump2png -k 10 core # Include one horiz line out of 10 (skip 9) 68 | 69 | You can always open the images up in an image editor (eg, gimp) and apply more 70 | effects. 71 | -------------------------------------------------------------------------------- /dump2png.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dump2png Visualize file data as a png. Intended for memory dumps. 3 | * 4 | * This tool is an experiment, intended to characterize the memory usage of 5 | * large process core dumps. It does by converting each byte to a colored 6 | * pixel, and building an image from these line by line. For serious core 7 | * dump analysis, look for other tools that read the metadata and structure 8 | * from the dump. 9 | * 10 | * USAGE: See: ./dump2png --help 11 | * 12 | * BUILD: gcc -O3 -lm -lpng -o dump2png dump2png.c # requires libpng 13 | * 14 | * By default, the least significant bit is masked, so that the image can't 15 | * be converted back to the input file, to avoid inadvertent privacy leaks. 16 | * Use -M to avoid masking, or increase BYTE_MASK to mask more bits. 17 | * 18 | * SEE ALSO: ImageMagick, which has similar functionality to the "gray" and 19 | * "rgb" palettes. 20 | * 21 | * Copyright 2012 Joyent, Inc. All rights reserved. 22 | * Copyright 2012 Brendan Gregg. All rights reserved. 23 | * 24 | * CDDL HEADER START 25 | * 26 | * The contents of this file are subject to the terms of the 27 | * Common Development and Distribution License (the "License"). 28 | * You may not use this file except in compliance with the License. 29 | * 30 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 31 | * or http://www.opensolaris.org/os/licensing. 32 | * See the License for the specific language governing permissions 33 | * and limitations under the License. 34 | * 35 | * When distributing Covered Code, include this CDDL HEADER in each 36 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 37 | * If applicable, add the following below this CDDL HEADER, with the 38 | * fields enclosed by brackets "[]" replaced with your own identifying 39 | * information: Portions Copyright [yyyy] [name of copyright owner] 40 | * 41 | * CDDL HEADER END 42 | * 43 | * 30-Apr-2012 Brendan Gregg Created this. 44 | */ 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | static void 56 | usage(int full) 57 | { 58 | printf("USAGE: dump2png [-HM] [-w width] [-h height_max]\n" 59 | " [-p palette] [-o outfile.png]\n" 60 | " [-k skip_factor] [-s seek_bytes]\n" 61 | " [-z zoom_factor] file\n\n" 62 | " [--help]\t# for full help\n\n" 63 | "palette types: gray, gray16b, gray16l, gray32b, gray32l,\n" 64 | " hues, hues6, fhues, color, color16, color32, rgb,\n" 65 | " dvi, x86 (default).\n"); 66 | if (!full) 67 | exit(1); 68 | printf("\n\t-H \tdon't autoscale height\n" 69 | "\t-M \tdon't mask least significant bit\n" 70 | "\t-k skip_factor\tskips horiz lines; eg, 3 means show 1 out of 3\n" 71 | "\t-s seek_bytes\tthe byte offset of the infile to begin reading\n" 72 | "\t-z zoom_factor\taverages multiple bytes; eg, 16 avgs 16 as 1\n" 73 | "\t-z palette\tpalette type for colorization:\n\n" 74 | "\tgray\t\tgrayscale, per byte\n" 75 | "\tgray16b\t\tgrayscale, per short (big-endian)\n" 76 | "\tgray16l\t\tgrayscale, per short (little-endian)\n" 77 | "\tgray32b\t\tgrayscale, per long (big-endian)\n" 78 | "\tgray32l\t\tgrayscale, per long (little-endian)\n" 79 | "\thues\t\tmap to 3 hue ranges (rgb), per byte (zoom safe)\n" 80 | "\thues6\t\tmap to 6 hue ranges (rgbcmy), per byte\n" 81 | "\tfhues\t\tmap to 3 full hue ranges (rgb), per byte (zoom safe)\n" 82 | "\tcolor\t\tfull colorized scale, per byte\n" 83 | "\tcolor16\t\tfull colorized scale, per short (16-bit)\n" 84 | "\tcolor32\t\tfull colorized scale, per long (32-bit)\n" 85 | "\trgb\t\ttreat 3 sequential bytes as RGB\n" 86 | "\tdvi\t\tuse RGB to convey differential, value, integral\n" 87 | "\tx86\t\tgrayscale with some (9) color indicators:\n\n" 88 | "\t green = common english chars: 'e', 't', 'a'\n" 89 | "\t red = common x86 instructions: movl, call, testl\n" 90 | "\t blue = binary values: 0x01, 0x02, 0x03\n"); 91 | exit(1); 92 | } 93 | 94 | typedef enum { 95 | GRAY = 0, 96 | GRAY16B, 97 | GRAY32B, 98 | GRAY16L, 99 | GRAY32L, 100 | HUES, 101 | HUES6, 102 | FHUES, 103 | COLOR, 104 | COLOR16, 105 | COLOR32, 106 | RGB, 107 | DVI, 108 | X86 109 | } palette_t; 110 | 111 | static palette_t atopal(const char *opt); 112 | static int pal2chrs(palette_t pal); 113 | static int doimage(int infile, FILE *outfile, int width, int height, 114 | palette_t pal, int skip, int zoom, int mask); 115 | 116 | int 117 | main(int argc, char *argv[]) 118 | { 119 | char *infilename, *outfilename = "dump2png.png"; 120 | extern char *optarg; 121 | extern int optind, optopt; 122 | struct stat filestat; 123 | int infile, opt, width, height, skip, zoom, chrs, mask, hscale = 1; 124 | palette_t pal; 125 | off_t seek; 126 | FILE *outfile; 127 | 128 | /* defaults */ 129 | width = 1024 * 1; 130 | height = 1024 * 10; 131 | zoom = skip = 1; 132 | seek = 0; 133 | mask = 1; 134 | pal = X86; 135 | 136 | if (argc < 2 || strcmp(argv[1], "--help") == 0) 137 | usage(argc >= 2); 138 | 139 | while ((opt = getopt(argc, argv, "HMh:k:o:p:s:w:z:?")) != EOF) { 140 | switch (opt) { 141 | case 'H': 142 | hscale = 0; 143 | break; 144 | case 'h': 145 | height = atoi(optarg); 146 | break; 147 | case 'k': 148 | skip = atoi(optarg); 149 | break; 150 | case 'M': 151 | mask = 0; 152 | break; 153 | case 'o': 154 | outfilename = optarg; 155 | break; 156 | case 'p': 157 | pal = atopal(optarg); 158 | break; 159 | case 's': 160 | seek = atoi(optarg); 161 | break; 162 | case 'w': 163 | width = atoi(optarg); 164 | break; 165 | case 'z': 166 | zoom = atoi(optarg); 167 | break; 168 | case '?': 169 | usage(0); 170 | } 171 | } 172 | 173 | if (width == 0 || height == 0 || skip == 0 || zoom == 0) 174 | usage(0); 175 | if (optind + 1 != argc) 176 | usage(0); 177 | infilename = argv[optind]; 178 | 179 | if (stat(infilename, &filestat) != 0) { 180 | perror("Can't access infile"); 181 | return (2); 182 | } 183 | 184 | chrs = pal2chrs(pal); 185 | int fullheight = ceil((float)(filestat.st_size / 186 | (zoom * skip * chrs)) / width); 187 | 188 | if (fullheight > height) { 189 | printf("Truncating height: showing %llu of %llu bytes. ", 190 | (unsigned long long)width * height * zoom * skip * chrs, 191 | (unsigned long long)filestat.st_size); 192 | printf("Use -h to allow larger heights.\n"); 193 | } else { 194 | if (hscale) { 195 | height = fullheight; 196 | } 197 | } 198 | 199 | printf("Output image: height:%d, width:%d\n", height, width); 200 | 201 | if ((infile = open(infilename, O_RDONLY)) < 0) { 202 | fprintf(stderr, "Can't read %s", infilename); 203 | exit(2); 204 | } 205 | 206 | if (seek && lseek(infile, seek, SEEK_SET) == -1) { 207 | perror("Seek failed"); 208 | exit(2); 209 | } 210 | 211 | outfile = fopen(outfilename, "wb"); 212 | if (outfile == NULL) { 213 | fprintf(stderr, "ERROR: Could not write to %s\n", outfilename); 214 | exit(2); 215 | } 216 | 217 | printf("Writing %s...\n", outfilename); 218 | int result = doimage(infile, outfile, width, height, pal, skip, zoom, 219 | mask); 220 | close(infile); 221 | fclose(outfile); 222 | 223 | return (result); 224 | } 225 | 226 | static palette_t 227 | atopal(const char *opt) 228 | { 229 | if (strcmp(opt, "gray") == 0) 230 | return (GRAY); 231 | if (strcmp(opt, "gray16b") == 0) 232 | return (GRAY16B); 233 | if (strcmp(opt, "gray32b") == 0) 234 | return (GRAY32B); 235 | if (strcmp(opt, "gray16l") == 0) 236 | return (GRAY16L); 237 | if (strcmp(opt, "gray32l") == 0) 238 | return (GRAY32L); 239 | if (strcmp(opt, "hues") == 0) 240 | return (HUES); 241 | if (strcmp(opt, "hues6") == 0) 242 | return (HUES6); 243 | if (strcmp(opt, "fhues") == 0) 244 | return (FHUES); 245 | if (strcmp(opt, "color") == 0) 246 | return (COLOR); 247 | if (strcmp(opt, "color16") == 0) 248 | return (COLOR16); 249 | if (strcmp(opt, "color32") == 0) 250 | return (COLOR32); 251 | if (strcmp(opt, "rgb") == 0) 252 | return (RGB); 253 | if (strcmp(opt, "dvi") == 0) 254 | return (DVI); 255 | if (strcmp(opt, "x86") == 0) 256 | return (X86); 257 | fprintf(stderr, "invalid palette. See USAGE (--help).\n"); 258 | exit(3); 259 | } 260 | 261 | static int 262 | pal2chrs(palette_t pal) 263 | { 264 | switch (pal) { 265 | case RGB: 266 | return (3); 267 | case GRAY16B: 268 | case GRAY16L: 269 | case COLOR16: 270 | return (2); 271 | case GRAY32B: 272 | case GRAY32L: 273 | case COLOR32: 274 | return (4); 275 | default: 276 | return (1); 277 | } 278 | } 279 | 280 | inline void 281 | map_hues(png_byte *ptr, unsigned char val) 282 | { 283 | int v = val * 3; 284 | if (v < 256) { 285 | ptr[0] = v; ptr[1] = 0; ptr[2] = 0; 286 | } else if (v < 512) { 287 | ptr[0] = 0; ptr[1] = v % 256; ptr[2] = 0; 288 | } else { 289 | ptr[0] = 0; ptr[1] = 0; ptr[2] = v % 256; 290 | } 291 | } 292 | 293 | inline void 294 | map_fhues(png_byte *ptr, unsigned char val) 295 | { 296 | int v = val * 6; 297 | if (v < 256) { 298 | ptr[0] = v; ptr[1] = 0; ptr[2] = 0; 299 | } else if (v < 256 * 2) { 300 | ptr[0] = 255; ptr[1] = v % 256; ptr[2] = v % 256; 301 | } else if (v < 256 * 3) { 302 | ptr[0] = 0; ptr[1] = v % 256; ptr[2] = 0; 303 | } else if (v < 256 * 4) { 304 | ptr[0] = v % 256; ptr[1] = 255; ptr[2] = v % 256; 305 | } else if (v < 256 * 5) { 306 | ptr[0] = 0; ptr[1] = 0; ptr[2] = v % 256; 307 | } else { 308 | ptr[0] = v % 256; ptr[1] = v % 256; ptr[2] = 255; 309 | } 310 | } 311 | 312 | inline void 313 | map_hues6(png_byte *ptr, unsigned char val) 314 | { 315 | int v = val * 6; 316 | if (v < 256) { 317 | ptr[0] = v; ptr[1] = 0; ptr[2] = 0; 318 | } else if (v < 256 * 2) { 319 | ptr[0] = 0; ptr[1] = v % 256; ptr[2] = 0; 320 | } else if (v < 256 * 3) { 321 | ptr[0] = 0; ptr[1] = 0; ptr[2] = v % 256; 322 | } else if (v < 256 * 4) { 323 | ptr[0] = 0; ptr[1] = v % 256; ptr[2] = v % 256; 324 | } else if (v < 256 * 5) { 325 | ptr[0] = v % 256; ptr[1] = 0; ptr[2] = v % 256; 326 | } else { 327 | ptr[0] = v % 256; ptr[1] = v % 256; ptr[2] = 0; 328 | } 329 | } 330 | 331 | inline void 332 | map_color16(png_byte *ptr, unsigned short val) 333 | { 334 | ptr[0] = (val & 0xfc00) >> 8; 335 | ptr[1] = (val & 0x03c0) >> 2; 336 | ptr[2] = (val & 0x001f) << 3; 337 | } 338 | 339 | inline void 340 | map_color32(png_byte *ptr, unsigned long val) 341 | { 342 | ptr[0] = (val & 0xff000000) >> 24; 343 | ptr[1] = (val & 0x001fe000) >> 13; 344 | ptr[2] = (val & 0x000001fe) >> 1; 345 | } 346 | 347 | inline unsigned char 348 | c2v_binary(unsigned char c) 349 | { 350 | switch (c) { 351 | case 0x01: return (0xff); 352 | case 0x02: return (0xcf); 353 | case 0x03: return (0xaf); 354 | } 355 | return (0); 356 | } 357 | 358 | inline unsigned char 359 | c2v_english(char c) 360 | { 361 | switch (c) { 362 | case 'e': return (0xff); 363 | case 't': return (0xcf); 364 | case 'a': return (0xaf); 365 | } 366 | return (0); 367 | } 368 | 369 | inline unsigned char 370 | c2v_x86(unsigned char c) 371 | { 372 | switch (c) { 373 | case 0x8b: return (0xff); /* movl */ 374 | case 0xe8: return (0xcf); /* call */ 375 | case 0x85: return (0xaf); /* testl */ 376 | } 377 | return (0); 378 | } 379 | 380 | static void 381 | map_x86(unsigned char *rgb, unsigned char c) 382 | { 383 | rgb[0] = rgb[1] = rgb[2] = 0; 384 | 385 | rgb[0] = c2v_x86(c); 386 | rgb[1] = c2v_english(c); 387 | rgb[2] = c2v_binary(c); 388 | 389 | /* default to grayscale */ 390 | if ((rgb[0] + rgb[1] + rgb[2]) == 0) { 391 | rgb[0] = rgb[1] = rgb[2] = c; 392 | } 393 | } 394 | 395 | #define BYTE_MASK 0xfe 396 | 397 | static int 398 | doimage(int infile, FILE *outfile, int width, int height, palette_t pal, 399 | int skip, int zoom, int mask) 400 | { 401 | png_text pngtitle; 402 | png_structp pngstruct; 403 | png_infop pnginfo; 404 | png_bytep pngbyte; 405 | unsigned char last, rgb[3], *inbuf; 406 | int in, xx, x, y, z, chrs, i = 0, code = 1; 407 | unsigned long sum[3]; 408 | 409 | /* setup png */ 410 | pngbyte = (png_bytep)malloc(width * skip * zoom * sizeof (png_byte) * 411 | 3); 412 | pngstruct = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, 413 | NULL); 414 | pnginfo = png_create_info_struct(pngstruct); 415 | chrs = pal2chrs(pal); 416 | inbuf = (char *)malloc(width * skip * zoom * chrs); 417 | 418 | if (pngbyte == NULL | pngstruct == NULL | pnginfo == NULL | 419 | inbuf == NULL) { 420 | perror("Out of memory"); 421 | goto out; 422 | } 423 | 424 | if (setjmp(png_jmpbuf(pngstruct))) { 425 | perror("Error during png creation"); 426 | goto out; 427 | } 428 | 429 | png_init_io(pngstruct, outfile); 430 | png_set_IHDR(pngstruct, pnginfo, width, height, 8, PNG_COLOR_TYPE_RGB, 431 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, 432 | PNG_FILTER_TYPE_BASE); 433 | 434 | pngtitle.compression = PNG_TEXT_COMPRESSION_NONE; 435 | pngtitle.key = "Title"; 436 | pngtitle.text = "dump2png"; 437 | png_set_text(pngstruct, pnginfo, &pngtitle, 1); 438 | 439 | png_write_info(pngstruct, pnginfo); 440 | 441 | /* 442 | * Read data and convert to png image. x tracks the destination pixel 443 | * x offset. xx tracks the offset in the input buffer, which can step 444 | * at a faster rate when it's necessary to combine multiple bytes into 445 | * one pixel (with zoom or certain palettes). 446 | */ 447 | for (y = 0; y < height; y++) { 448 | in = read(infile, inbuf, width * chrs * skip * zoom); 449 | 450 | for (x = 0, xx = 0; x < width; x++) { 451 | if (xx + chrs > in) { 452 | (&pngbyte[x * 3])[0] = 0; 453 | (&pngbyte[x * 3])[1] = 0; 454 | (&pngbyte[x * 3])[2] = 0; 455 | continue; 456 | } 457 | 458 | sum[0] = sum[1] = sum[2] = 0; 459 | 460 | for (z = 0; z < zoom; z++, xx++) { 461 | switch (pal) { 462 | case GRAY: 463 | rgb[0] = inbuf[xx]; 464 | rgb[1] = inbuf[xx]; 465 | rgb[2] = inbuf[xx]; 466 | break; 467 | /* 468 | * Gray 16|32 skip bytes and map 469 | * significant byte to grayscale. 470 | */ 471 | case GRAY16B: 472 | rgb[0] = inbuf[xx]; 473 | rgb[1] = inbuf[xx]; 474 | rgb[2] = inbuf[xx++]; 475 | break; 476 | case GRAY32B: 477 | rgb[0] = inbuf[xx]; 478 | rgb[1] = inbuf[xx]; 479 | rgb[2] = inbuf[xx]; 480 | xx += 3; 481 | break; 482 | case GRAY16L: 483 | rgb[0] = inbuf[++xx]; 484 | rgb[1] = inbuf[xx]; 485 | rgb[2] = inbuf[xx]; 486 | break; 487 | case GRAY32L: 488 | xx += 3; 489 | rgb[0] = inbuf[xx]; 490 | rgb[1] = inbuf[xx]; 491 | rgb[2] = inbuf[xx]; 492 | break; 493 | case HUES: 494 | map_hues(&rgb[0], inbuf[xx]); 495 | break; 496 | case HUES6: 497 | map_hues6(&rgb[0], inbuf[xx]); 498 | break; 499 | case FHUES: 500 | map_fhues(&rgb[0], inbuf[xx]); 501 | break; 502 | /* 503 | * Color palettes mask and shifts bits 504 | * into RGB 505 | */ 506 | case COLOR: 507 | rgb[0] = inbuf[xx] & 0xe0; 508 | rgb[1] = (inbuf[xx] & 0x1c) << 509 | 3; 510 | rgb[2] = (inbuf[xx] & 0x03) << 511 | 6; 512 | break; 513 | case COLOR16: 514 | map_color16(&rgb[0], 515 | inbuf[xx++] + 516 | (inbuf[xx] << 8)); 517 | break; 518 | case COLOR32: 519 | map_color32(&rgb[0], 520 | inbuf[xx++] + 521 | (inbuf[xx++] << 8) + 522 | (inbuf[xx++] << 16) + 523 | (inbuf[xx] << 24)); 524 | break; 525 | /* 526 | * RGB uses sequential bytes for RGB 527 | */ 528 | case RGB: 529 | rgb[0] = inbuf[xx++]; 530 | rgb[1] = inbuf[xx++]; 531 | rgb[2] = inbuf[xx]; 532 | break; 533 | case X86: 534 | map_x86(&rgb[0], inbuf[xx]); 535 | break; 536 | case DVI: 537 | rgb[0] = abs(inbuf[xx] - last); 538 | rgb[1] = inbuf[xx]; 539 | rgb[2] = (inbuf[xx] + last) / 2; 540 | break; 541 | default: 542 | fprintf(stderr, "palette?\n"); 543 | goto out; 544 | } 545 | 546 | if (zoom > 1) { 547 | sum[0] += rgb[0]; 548 | sum[1] += rgb[1]; 549 | sum[2] += rgb[2]; 550 | } 551 | } 552 | 553 | if (zoom > 1) { 554 | rgb[0] = sum[0] / zoom; 555 | rgb[1] = sum[1] / zoom; 556 | rgb[2] = sum[2] / zoom; 557 | } 558 | 559 | if (mask) { 560 | rgb[0] = rgb[0] & BYTE_MASK; 561 | rgb[1] = rgb[1] & BYTE_MASK; 562 | rgb[2] = rgb[2] & BYTE_MASK; 563 | } 564 | 565 | (&pngbyte[x * 3])[0] = rgb[0]; 566 | (&pngbyte[x * 3])[1] = rgb[1]; 567 | (&pngbyte[x * 3])[2] = rgb[2]; 568 | 569 | last = inbuf[x]; 570 | } 571 | png_write_row(pngstruct, pngbyte); 572 | } 573 | 574 | png_write_end(pngstruct, NULL); 575 | code = 0; 576 | 577 | out: 578 | if (pnginfo != NULL) 579 | png_free_data(pngstruct, pnginfo, PNG_FREE_ALL, -1); 580 | if (pngstruct != NULL) 581 | png_destroy_write_struct(&pngstruct, (png_infopp) NULL); 582 | if (pngbyte != NULL) 583 | free(pngbyte); 584 | 585 | return (code); 586 | } 587 | --------------------------------------------------------------------------------