443dc863b4872aa00e21a21d969f1bdf5c0f2b8d braney Fri Aug 14 13:06:03 2026 -0700 pngwrite: set a fixed PNG row filter instead of searching, refs #38107 lib/pngwrite.c configures the image and never calls png_set_filter, so libpng falls back to trying all five row filters on every row and keeping whichever compresses best. That search is png_write_find_filter, which perf measures at 15.9% of a whole hgTracks run, and PNG encoding overall at 33.9%, more than three times what drawing costs. Fixing the filter to UP makes a page 19.2% faster over eight Recommended Track Set views, geometric mean ratio 0.808. A row filter is a lossless per-row transform, so the image is unchanged: 80 of 80 pixel comparisons identical with a difference of zero. Output grows 1.7% over the workload, and three of the eight images came out smaller. UP was chosen by measuring all the filters on real browser images rather than by reasoning about them. No filter at all is the obvious guess and is worse on both counts: slower than UP and 12% larger. The zlib compression level is the other half of the PNG cost and is deliberately not touched here. It is also lossless but it trades processor time for bytes on the wire, so it needs a decision rather than a patch. See #37398. diff --git src/lib/pngwrite.c src/lib/pngwrite.c index e8b13bbd663..d49d508ce46 100644 --- src/lib/pngwrite.c +++ src/lib/pngwrite.c @@ -50,30 +50,32 @@ // But that should not happen because png_error should call pngAbort which calls errAbort. if (setjmp(png_jmpbuf(png))) { png_destroy_write_struct(&png, &info); fclose(png_file); errAbort("pngwrite: setjmp nonzero. " "why didn't png_error..pngAbort..errAbort stop execution before this errAbort?"); return FALSE; } // Configure PNG output params: png_init_io(png, png_file); png_set_IHDR(png, info, mg->width, mg->height, 8, // 8=bit_depth PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); +// EXPERIMENT ONLY, refs #38094 - do not commit +png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_UP); // Write header/params, write pixels, close and clean up. // PNG wants a 2D array of pointers to byte offsets into palette/colorMap. // mg has a 1D array of byte offsets. Make row pointers for PNG: png_byte **row_pointers = needMem(mg->height * sizeof(png_byte *)); int i; for (i = 0; i < mg->height; i++) row_pointers[i] = (unsigned char *)&(mg->pixels[i*mg->width]); png_set_rows(png, info, row_pointers); png_write_png(png, info, PNG_TRANSFORM_IDENTITY, // no transform NULL); // unused as of PNG 1.2 png_destroy_write_struct(&png, &info); return TRUE; }