X-Git-Url: http://git.tdb.fi/?p=ttf2png.git;a=blobdiff_plain;f=ttf2png.c;h=770f0c633119efc6a3e6219a287bb85de48a193e;hp=d9869bf173671caa4b6908f8f1cd357cb0e978ad;hb=HEAD;hpb=f506acab628d313bb2e140ec936dc8970ba2f0d0 diff --git a/ttf2png.c b/ttf2png.c index d9869bf..76c2e0d 100644 --- a/ttf2png.c +++ b/ttf2png.c @@ -1,6 +1,6 @@ /* ttf2png - True Type Font to PNG converter -Copyright (c) 2004-2008 Mikko Rasa +Copyright (c) 2004-2021 Mikko Rasa, Mikkosoft Productions 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 @@ -28,7 +28,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA typedef struct sImage { unsigned w, h; - char *data; + unsigned char *data; + unsigned border; } Image; typedef struct sGlyph @@ -44,8 +45,8 @@ typedef struct sGlyph typedef struct sKerning { - unsigned left_code; - unsigned right_code; + Glyph *left_glyph; + Glyph *right_glyph; int distance; } Kerning; @@ -61,27 +62,41 @@ typedef struct sFont Image image; } Font; -typedef int bool; +typedef struct sRange +{ + unsigned first; + unsigned last; +} Range; + +typedef unsigned char bool; void usage(void); int convert_numeric_option(char, int); -void convert_code_point_range(char, unsigned *, unsigned *); +void convert_code_point_range(char, Range *); +int str_to_code_point(const char *, char **); void convert_size(char, unsigned *, unsigned *); +void sort_and_compact_ranges(Range *, unsigned *); +int range_cmp(const void *, const void *); unsigned round_to_pot(unsigned); -void *alloc_image_data(size_t, size_t); -int init_font(Font *, FT_Face, unsigned, unsigned, bool); +int init_image(Image *, size_t, size_t); +int init_font(Font *, FT_Face, const Range *, unsigned, bool, unsigned, unsigned); +int init_glyphs(Font *, FT_Face, const Range *, bool, unsigned, unsigned); +int copy_bitmap(const FT_Bitmap *, Image *); +unsigned sqrti(unsigned); +unsigned find_distance_to_edge(const Image *, int, int, unsigned); +int create_distance_field(const FT_Bitmap *, Image *, unsigned, unsigned); int render_grid(Font *, unsigned, unsigned, unsigned, bool); int render_packed(Font *, unsigned, unsigned); int save_defs(const char *, const Font *); -int save_png(const char *, const Image *, char); +int save_png(const char *, const Image *, bool, bool, bool); char verbose = 0; int main(int argc, char **argv) { char *fn; - unsigned begin = 0; - unsigned end = 255; + Range *ranges = NULL; + unsigned n_ranges = 0; unsigned size = 10; unsigned cpl = 0; unsigned cellw = 0; @@ -93,6 +108,9 @@ int main(int argc, char **argv) bool pack = 0; unsigned margin = 0; unsigned padding = 1; + bool npot = 0; + unsigned distfield = 0; + unsigned border = 0; FT_Library freetype; FT_Face face; @@ -111,12 +129,13 @@ int main(int argc, char **argv) return 1; } - while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:")) != -1) + while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:gf:b:")) != -1) { switch(i) { case 'r': - convert_code_point_range('r', &begin, &end); + ranges = (Range *)realloc(ranges, (++n_ranges)*sizeof(Range)); + convert_code_point_range('r', &ranges[n_ranges-1]); break; case 's': size = convert_numeric_option('s', 1); @@ -161,6 +180,15 @@ int main(int argc, char **argv) case 'n': padding = convert_numeric_option('n', 0); break; + case 'g': + npot = 1; + break; + case 'f': + distfield = convert_numeric_option('f', 1); + break; + case 'b': + border = convert_numeric_option('b', 1); + break; } } if(!strcmp(out_fn, "-")) @@ -197,6 +225,14 @@ int main(int argc, char **argv) printf("Glyphs: %ld\n", face->num_glyphs); } + font.size = size; + if(distfield) + { + if(!border) + border = sqrti(font.size); + size *= distfield; + } + err = FT_Set_Pixel_Sizes(face, 0, size); if(err) { @@ -204,11 +240,26 @@ int main(int argc, char **argv) return 1; } - font.size = size; - err = init_font(&font, face, begin, end, autohinter); + if(!n_ranges) + { + ranges = malloc(sizeof(Range)); + ranges[0].first = 0; + ranges[0].last = 255; + n_ranges = 1; + } + else + sort_and_compact_ranges(ranges, &n_ranges); + + err = init_font(&font, face, ranges, n_ranges, autohinter, distfield, border); if(err) return 1; + if(!font.n_glyphs) + { + fprintf(stderr, "No glyphs found in the requested range\n"); + return 1; + } + if(pack) err = render_packed(&font, margin, padding); else @@ -216,12 +267,7 @@ int main(int argc, char **argv) if(err) return 1; - if(invert) - { - for(i=0; (unsigned)i\n\n"); @@ -262,6 +309,9 @@ void usage(void) " -p Pack the glyphs tightly instead of in a grid\n" " -m Margin around image edges (packed mode only) [0]\n" " -n Padding between glyphs (packed mode only) [1]\n" + " -g Allow non-power-of-two result\n"); + printf(" -f Create a distance field texture\n" + " -b Specify distance field border zone width\n" " -d File name for writing glyph definitions\n" " -h Print this message\n"); } @@ -281,26 +331,26 @@ int convert_numeric_option(char opt, int min_value) return value; } -void convert_code_point_range(char opt, unsigned *begin, unsigned *end) +void convert_code_point_range(char opt, Range *range) { int value; char *ptr; if(!strcmp(optarg, "all")) { - *begin = 0; - *end = 0x10FFFF; + range->first = 0; + range->last = 0x10FFFF; return; } - value = strtol(optarg, &ptr, 0); - if(value>0 && *ptr==',') + value = str_to_code_point(optarg, &ptr); + if(value>=0 && *ptr==',') { - *begin = value; - value = strtol(ptr+1, &ptr, 0); - if(value>0 && !*ptr) + range->first = value; + value = str_to_code_point(ptr+1, &ptr); + if(value>=(int)range->first && !*ptr) { - *end = value; + range->last = value; return; } } @@ -309,6 +359,44 @@ void convert_code_point_range(char opt, unsigned *begin, unsigned *end) exit(1); } +int str_to_code_point(const char *nptr, char **endptr) +{ + if(nptr[0]=='U' && nptr[1]=='+') + return strtoul(nptr+2, endptr, 16); + else if(nptr[0]&0x80) + { + unsigned bytes; + unsigned code; + unsigned i; + + if(endptr) + *endptr = (char *)nptr; + + for(bytes=1; (bytes<4 && (nptr[0]&(0x80>>bytes))); ++bytes) + if((nptr[bytes]&0xC0)!=0x80) + return -1; + if(bytes<2) + return -1; + + code = nptr[0]&(0x3F>>bytes); + for(i=1; i=ranges[j].first) + { + if(ranges[j].last>ranges[i].last) + ranges[i].last = ranges[j].last; + } + else + { + ++i; + if(i!=j) + ranges[i] = ranges[j]; + } + } + + *n_ranges = i+1; +} + +int range_cmp(const void *p1, const void *p2) +{ + const Range *r1 = (const Range *)p1; + const Range *r2 = (const Range *)p2; + if(r1->first!=r2->first) + return (r1->firstfirst ? -1 : 1); + else if(r1->last!=r2->last) + return (r1->lastlast ? -1 : 1); + else + return 0; +} + unsigned round_to_pot(unsigned n) { n -= 1; @@ -363,37 +489,43 @@ unsigned round_to_pot(unsigned n) return n+1; } -void *alloc_image_data(size_t a, size_t b) +int init_image(Image *image, size_t w, size_t h) { - void *ptr; + size_t s; + + image->w = w; + image->h = h; + image->data = NULL; + image->border = 0; + + if(!image->w || !image->h) + return 0; - /* Carry out the multiplication manually so we can check for overflow. */ - while(b>1) + s = w*h; + if(s/h!=w) { - size_t c = a; - a *= 2; - if(b&1) - a += c; - if(aw, image->h); + return -1; } - ptr = malloc(a); - if(!ptr) - fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b)); - return ptr; + + image->data = malloc(s); + if(!image->data) + { + fprintf(stderr, "Cannot allocate memory for a %dx%d image\n", image->w, image->h); + return -1; + } + + return 0; } -int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool autohinter) +int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter, unsigned distfield, unsigned border) { unsigned i, j; unsigned size = 0; + int scale = (distfield>0 ? distfield : 1); - font->ascent = (face->size->metrics.ascender+63)>>6; - font->descent = (face->size->metrics.descender+63)>>6; + font->ascent = (face->size->metrics.ascender/scale+63)/64; + font->descent = (face->size->metrics.descender/scale-63)/64; if(verbose>=1) { @@ -403,11 +535,56 @@ int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool auto font->n_glyphs = 0; font->glyphs = NULL; - for(i=first; i<=last; ++i) + for(i=0; i=1) + printf("Loaded %u glyphs\n", font->n_glyphs); + + font->n_kerning = 0; + font->kerning = NULL; + for(i=0; in_glyphs; ++i) for(j=0; jn_glyphs; ++j) + if(j!=i) + { + FT_Vector kerning; + FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning); + + /* FreeType documentation says that vertical kerning is practically + never used, so we ignore it. */ + if(kerning.x) + { + Kerning *kern; + + if(font->n_kerning>=size) + { + size += 16; + font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning)); + } + + kern = &font->kerning[font->n_kerning++]; + kern->left_glyph = &font->glyphs[i]; + kern->right_glyph = &font->glyphs[j]; + kern->distance = (kerning.x/scale+32)/64; + } + } + + if(verbose>=1) + printf("Loaded %d kerning pairs\n", font->n_kerning); + + return 0; +} + +int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter, unsigned distfield, unsigned border) +{ + unsigned i, j; + unsigned size = font->n_glyphs; + int scale = (distfield>0 ? distfield : 1); + + for(i=range->first; i<=range->last; ++i) { unsigned n; FT_Bitmap *bmp = &face->glyph->bitmap; - unsigned x, y; int flags = 0; Glyph *glyph; @@ -418,14 +595,32 @@ int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool auto if(autohinter) flags |= FT_LOAD_FORCE_AUTOHINT; FT_Load_Glyph(face, n, flags); - FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + FT_Render_Glyph(face->glyph, (distfield ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL)); if(verbose>=2) - printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows); + { + printf(" Code point U+%04X", i); + if(i>=0x20 && i<0x7F) + printf(" (%c)", i); + else if(i>=0xA0 && i<=0x10FFFF) + { + char utf8[5]; + unsigned bytes; - if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY) + for(bytes=2; i>>(1+bytes*5); ++bytes) ; + for(j=0; j>((bytes-j-1)*6))&0x3F); + utf8[0] |= 0xF0<<(4-bytes); + utf8[j] = 0; + + printf(" (%s)", utf8); + } + printf(": glyph %u, size %dx%d\n", n, bmp->width/scale, bmp->rows/scale); + } + + if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY && bmp->pixel_mode!=FT_PIXEL_MODE_MONO) { - fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n); + fprintf(stderr, "Warning: Glyph %u skipped, incompatible pixel mode\n", n); continue; } @@ -438,66 +633,138 @@ int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool auto glyph = &font->glyphs[font->n_glyphs++]; glyph->index = n; glyph->code = i; - glyph->image.w = bmp->width; - glyph->image.h = bmp->rows; - glyph->image.data = (char *)malloc(bmp->width*bmp->rows); - if(!glyph->image.data) - { - fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows); - return -1; - } - glyph->offset_x = face->glyph->bitmap_left; - glyph->offset_y = face->glyph->bitmap_top-bmp->rows; - glyph->advance = (int)(face->glyph->advance.x+32)/64; + glyph->offset_x = (int)(face->glyph->bitmap_left+scale/2)/scale; + glyph->offset_y = (int)(face->glyph->bitmap_top-bmp->rows+scale/2)/scale; + glyph->advance = (int)(face->glyph->advance.x/scale+32)/64; /* Copy the glyph image since FreeType uses a global buffer, which would be overwritten by the next glyph. Negative pitch means the scanlines start from the bottom. */ - if(bmp->pitch<0) + if(distfield) { - for(y=0; yrows; ++y) for(x=0; xwidth; ++x) - glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch]; + glyph->offset_x -= border; + glyph->offset_y -= border; + create_distance_field(bmp, &glyph->image, distfield, border); } else { - for(y=0; yrows; ++y) for(x=0; xwidth; ++x) - glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch]; + if(copy_bitmap(bmp, &glyph->image)) + return -1; } } - if(verbose>=1) - printf("Loaded %u glyphs\n", font->n_glyphs); + return 0; +} - size = 0; - font->n_kerning = 0; - font->kerning = NULL; - for(i=0; in_glyphs; ++i) for(j=0; jn_glyphs; ++j) - if(j!=i) +int copy_bitmap(const FT_Bitmap *bmp, Image *image) +{ + unsigned x, y; + unsigned char *src; + unsigned char *dst; + + if(init_image(image, bmp->width, bmp->rows)) + return -1; + if(!image->w || !image->h) + return 0; + + if(bmp->pitch<0) + src = bmp->buffer+(bmp->rows-1)*-bmp->pitch; + else + src = bmp->buffer; + dst = image->data; + + for(y=0; yrows; ++y) + { + if(bmp->pixel_mode==FT_PIXEL_MODE_MONO) { - FT_Vector kerning; - FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning); + for(x=0; xwidth; ++x) + dst[x] = ((src[x/8]&(0x80>>(x%8))) ? 0xFF : 0x00); + } + else + { + for(x=0; xwidth; ++x) + dst[x] = src[x]; + } - /* FreeType documentation says that vertical kerning is practically - never used, so we ignore it. */ - if(kerning.x) - { - Kerning *kern; + src += bmp->pitch; + dst += image->w; + } - if(font->n_kerning>=size) - { - size += 16; - font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning)); - } + return 0; +} - kern = &font->kerning[font->n_kerning++]; - kern->left_code = font->glyphs[i].code; - kern->right_code = font->glyphs[j].code; - kern->distance = kerning.x/64; +unsigned sqrti(unsigned num) +{ + unsigned result = (num>0xFFFF ? 0xFFFF : 0x100); + while(result && result*result>=result+num) + result -= (result*result+result-num)/(result*2); + + return result; +} + +unsigned find_distance_to_edge(const Image *image, int origin_x, int origin_y, unsigned range) +{ + unsigned i, j; + int x, y; + unsigned char origin_pixel = 0; + unsigned closest = range*range; + + if(origin_x>=0 && (unsigned)origin_xw && origin_y>=0 && (unsigned)origin_yh) + origin_pixel = image->data[origin_x+origin_y*image->w]; + + x = origin_x-1; + y = origin_y-1; + for(i=1; (i=0 && (unsigned)xw && y>=0 && (unsigned)yh) + pixel = image->data[x+y*image->w]; + + if((pixel^origin_pixel)&0x80) + { + unsigned d = 2*i*i + k*k - 2*k*i; + if(d=1) - printf("Loaded %d kerning pairs\n", font->n_kerning); + return sqrti(closest*0x3F01)/range; +} + +int create_distance_field(const FT_Bitmap *bmp, Image *image, unsigned scale, unsigned margin) +{ + unsigned x, y; + Image base_image; + + if(init_image(image, (bmp->width+scale-1)/scale+2*margin, (bmp->rows+scale-1)/scale+2*margin)) + return -1; + if(!image->w || !image->h) + return 0; + + if(copy_bitmap(bmp, &base_image)) + return -1; + + image->border = margin; + for(y=0; yh; ++y) for(x=0; xw; ++x) + { + int bx = (x-margin)*scale+scale/2; + int by = (y-margin)*scale+scale/2; + unsigned char pixel = find_distance_to_edge(&base_image, bx, by, margin*scale); + if(bx>=0 && (unsigned)bx=0 && (unsigned)bydata[x+y*image->w] = pixel; + } + + free(base_image.data); return 0; } @@ -506,7 +773,7 @@ int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool s { unsigned i; int top = 0, bot = 0; - unsigned first, last; + unsigned first, n_cells; unsigned maxw = 0, maxh = 0; /* Find extremes of the glyph images. */ @@ -562,20 +829,17 @@ int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool s } first = font->glyphs[0].code; - if(!seq) - first -= first%cpl; - last = font->glyphs[font->n_glyphs-1].code; - - font->image.w = round_to_pot(cpl*cellw); if(seq) - font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh); + n_cells = font->n_glyphs; else - font->image.h = round_to_pot((last-first+cpl)/cpl*cellh); + { + first -= first%cpl; + n_cells = font->glyphs[font->n_glyphs-1].code+1-first; + } - font->image.data = (char *)alloc_image_data(font->image.w, font->image.h); - if(!font->image.data) + if(init_image(&font->image, cpl*cellw, (n_cells+cpl-1)/cpl*cellh)) return -1; - memset(font->image.data, 255, font->image.w*font->image.h); + memset(font->image.data, 0, font->image.w*font->image.h); for(i=0; in_glyphs; ++i) { @@ -604,7 +868,7 @@ int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool s { if(cx+x>=font->image.w || cy+y>=font->image.h) continue; - font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w]; + font->image.data[cx+x+(cy+y)*font->image.w] = glyph->image.data[x+y*glyph->image.w]; } } @@ -615,7 +879,7 @@ int render_packed(Font *font, unsigned margin, unsigned padding) { unsigned i; size_t area = 0; - char *used_glyphs; + bool *used_glyphs; unsigned *used_pixels; unsigned cx = margin, cy; unsigned used_h = 0; @@ -632,29 +896,29 @@ int render_packed(Font *font, unsigned margin, unsigned padding) area = a; } - /* Find an image size that's no higher than wide, allowing for some - imperfections in the packing. */ + /* Find an image size that's approximately square. */ for(font->image.w=1;; font->image.w<<=1) { if(font->image.w<=margin*2) continue; - font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2; + font->image.h = area/(font->image.w-margin*2)+margin*2; if(font->image.h<=font->image.w) break; } - font->image.h = round_to_pot(font->image.h); + + /* Add some extra space to accommodate packing imperfections. */ + font->image.h = font->image.h*3/2; /* Allocate arrays for storing the image and keeping track of used pixels and glyphs. Since glyphs are rectangular and the image is filled starting from the top, it's enough to track the number of used pixels at the top of each column. */ - font->image.data = (char *)alloc_image_data(font->image.w, font->image.h); - if(!font->image.data) + if(init_image(&font->image, font->image.w, font->image.h)) return -1; - memset(font->image.data, 255, font->image.w*font->image.h); + memset(font->image.data, 0, font->image.w*font->image.h); used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned)); memset(used_pixels, 0, font->image.w*sizeof(unsigned)); - used_glyphs = (char *)malloc(font->n_glyphs); + used_glyphs = (bool *)malloc(font->n_glyphs); memset(used_glyphs, 0, font->n_glyphs); for(cy=margin; cy+marginimage.h;) @@ -722,7 +986,7 @@ int render_packed(Font *font, unsigned margin, unsigned padding) { if(cx+x>=font->image.w || cy+y>=font->image.h) continue; - font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w]; + font->image.data[cx+x+(cy+y)*font->image.w] = glyph->image.data[x+y*glyph->image.w]; } for(x=0; ximage.w+2*padding; ++x) { @@ -738,7 +1002,7 @@ int render_packed(Font *font, unsigned margin, unsigned padding) /* Trim the image to the actually used size, in case the original estimate was too pessimistic. */ - font->image.h = round_to_pot(used_h); + font->image.h = used_h; free(used_glyphs); free(used_pixels); @@ -761,19 +1025,38 @@ int save_defs(const char *fn, const Font *font) fprintf(out, "# Image/font info:\n"); fprintf(out, "# width height size ascent descent\n"); fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent); + + fprintf(out, "\n# Code point mapping:\n"); + fprintf(out, "# code index\n"); + for(i=0; in_glyphs; ++i) + { + const Glyph *g = &font->glyphs[i]; + fprintf(out, "code %u %u\n", g->code, g->index); + } + + fprintf(out, "\n# Metrics info:\n"); + fprintf(out, "# index width height offset_x offset_y advance\n"); + for(i=0; in_glyphs; ++i) + { + const Glyph *g = &font->glyphs[i]; + int b = g->image.border; + fprintf(out, "metrics %u %u %u %d %d %d\n", g->index, g->image.w-2*b, g->image.h-2*b, g->offset_x+b, g->offset_y+b, g->advance); + } + fprintf(out, "\n# Glyph info:\n"); - fprintf(out, "# code x y width height offset_x offset_y advance\n"); + fprintf(out, "# index x y width height border\n"); for(i=0; in_glyphs; ++i) { const Glyph *g = &font->glyphs[i]; - fprintf(out, "glyph %u %u %u %u %u %d %d %d\n", g->code, g->x, g->y, g->image.w, g->image.h, g->offset_x, g->offset_y, g->advance); + fprintf(out, "glyph %u %u %u %u %u %u\n", g->index, g->x, g->y, g->image.w, g->image.h, g->image.border); } + fprintf(out, "\n# Kerning info:\n"); fprintf(out, "# left right distance\n"); for(i=0; in_kerning; ++i) { const Kerning *k = &font->kerning[i]; - fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance); + fprintf(out, "kern %u %u %d\n", k->left_glyph->index, k->right_glyph->index, k->distance); } fclose(out); @@ -781,15 +1064,17 @@ int save_defs(const char *fn, const Font *font) return 0; } -int save_png(const char *fn, const Image *image, char alpha) +int save_png(const char *fn, const Image *image, bool alpha, bool invert, bool npot) { FILE *out; png_struct *pngs; png_info *pngi; - png_byte **rows; - unsigned i; - png_byte *data2 = 0; + unsigned w, h; + png_byte *row; + unsigned x, y; int color; + unsigned flip_bits = (invert==alpha ? 0xFF : 0x00); + unsigned char *src = image->data; if(!strcmp(fn, "-")) out = stdout; @@ -817,38 +1102,53 @@ int save_png(const char *fn, const Image *image, char alpha) return -1; } + w = (npot ? image->w : round_to_pot(image->w)); + h = (npot ? image->h : round_to_pot(image->h)); + color = (alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY); + png_set_IHDR(pngs, pngi, w, h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_init_io(pngs, out); - rows = (png_byte **)malloc(image->h*sizeof(png_byte *)); + png_write_info(pngs, pngi); + row = (png_byte *)malloc(w*(1+alpha)); if(alpha) { - data2 = (png_byte *)alloc_image_data(image->w*2, image->h); - if(!data2) - return -1; - for(i=0; iw*image->h; ++i) + for(x=0; xdata[i]; + row[x*2] = 255; + row[x*2+1] = flip_bits; } - for(i=0; ih; ++i) - rows[i] = (png_byte *)(data2+i*image->w*2); - color = PNG_COLOR_TYPE_GRAY_ALPHA; + for(y=0; yh; ++y) + { + for(x=0; xw; ++x) + row[x*2+1] = *src++^flip_bits; + png_write_row(pngs, row); + } + + for(x=0; xh; ++i) - rows[i] = (png_byte *)(image->data+i*image->w); - color = PNG_COLOR_TYPE_GRAY; + memset(row+image->w, flip_bits, w-image->w); + for(y=0; yh; ++y) + { + for(x=0; xw; ++x) + row[x] = *src++^flip_bits; + png_write_row(pngs, row); + } + + memset(row, flip_bits, w); } - png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_set_rows(pngs, pngi, rows); - png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL); + + for(; yw, image->h, fn); + printf("Saved %dx%d PNG image to %s\n", w, h, fn); fclose(out);