From 7387ec23ac22558809f67fbaf2dc3076a0fa8939 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 31 Mar 2021 08:40:28 +1100 Subject: [PATCH 1/4] Revert "Removed return value of build_distance_tables" This reverts commit a4a38b805b524f0fdb0d1feca83ca73d3ccfff0b. --- src/libImaging/Quant.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index 8ec99699f..17b75b4c2 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -789,7 +789,7 @@ resort_distance_tables( return 1; } -static void +static int build_distance_tables( uint32_t *avgDist, uint32_t **avgDistSortKey, Pixel *p, uint32_t nEntries) { uint32_t i, j; @@ -811,6 +811,7 @@ build_distance_tables( sizeof(uint32_t *), _sort_ulong_ptr_keys); } + return 1; } static int @@ -1372,7 +1373,9 @@ quantize( goto error_6; } - build_distance_tables(avgDist, avgDistSortKey, p, nPaletteEntries); + if (!build_distance_tables(avgDist, avgDistSortKey, p, nPaletteEntries)) { + goto error_7; + } if (!map_image_pixels_from_median_box( pixelData, nPixels, p, nPaletteEntries, h, avgDist, avgDistSortKey, qp)) { @@ -1577,7 +1580,9 @@ quantize2( goto error_3; } - build_distance_tables(avgDist, avgDistSortKey, p, nQuantPixels); + if (!build_distance_tables(avgDist, avgDistSortKey, p, nQuantPixels)) { + goto error_4; + } if (!map_image_pixels( pixelData, nPixels, p, nQuantPixels, avgDist, avgDistSortKey, qp)) { From 6541bd7cb5e43b78a88b16cb1cd0255db11f07df Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 31 Mar 2021 09:05:19 +1100 Subject: [PATCH 2/4] Added second attribute to avoid unstable nature of qsort --- Tests/images/hopper_resized.gif | Bin 0 -> 5226 bytes Tests/test_image_resize.py | 18 +++++++++++- src/libImaging/Quant.c | 48 +++++++++++++++++++++++++------- 3 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 Tests/images/hopper_resized.gif diff --git a/Tests/images/hopper_resized.gif b/Tests/images/hopper_resized.gif new file mode 100644 index 0000000000000000000000000000000000000000..f7be6c26298faca89ddaf52335d21c1ff934b5ec GIT binary patch literal 5226 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 Yq}k_o*n>XS25Z{4GkSam3distance == *B->distance) { + return A->index < B->index ? -1 : +1; + } + return *A->distance < *B->distance ? -1 : +1; } static int @@ -793,24 +801,42 @@ static int build_distance_tables( uint32_t *avgDist, uint32_t **avgDistSortKey, Pixel *p, uint32_t nEntries) { uint32_t i, j; + DistanceWithIndex *dwi; + dwi = calloc(nEntries * nEntries, sizeof(DistanceWithIndex)); + if (!dwi) { + return 0; + } for (i = 0; i < nEntries; i++) { avgDist[i * nEntries + i] = 0; - avgDistSortKey[i * nEntries + i] = &(avgDist[i * nEntries + i]); + dwi[i * nEntries + i] = (DistanceWithIndex){ + &(avgDist[i * nEntries + i]), + i * nEntries + i + }; for (j = 0; j < i; j++) { avgDist[j * nEntries + i] = avgDist[i * nEntries + j] = _DISTSQR(p + i, p + j); - avgDistSortKey[j * nEntries + i] = &(avgDist[j * nEntries + i]); - avgDistSortKey[i * nEntries + j] = &(avgDist[i * nEntries + j]); + dwi[j * nEntries + i] = (DistanceWithIndex){ + &(avgDist[j * nEntries + i]), + j * nEntries + i + }; + dwi[i * nEntries + j] = (DistanceWithIndex){ + &(avgDist[i * nEntries + j]), + i * nEntries + j + }; } } for (i = 0; i < nEntries; i++) { qsort( - avgDistSortKey + i * nEntries, + dwi + i * nEntries, nEntries, - sizeof(uint32_t *), + sizeof(DistanceWithIndex), _sort_ulong_ptr_keys); + for (j = 0; j < nEntries; j++) { + avgDistSortKey[i * nEntries + j] = dwi[i * nEntries + j].distance; + } } + free(dwi); return 1; } @@ -1176,8 +1202,10 @@ k_means( if (!built) { compute_palette_from_quantized_pixels( pixelData, nPixels, paletteData, nPaletteEntries, avg, count, qp); - build_distance_tables( - avgDist, avgDistSortKey, paletteData, nPaletteEntries); + if (!build_distance_tables( + avgDist, avgDistSortKey, paletteData, nPaletteEntries)) { + goto error_3; + } built = 1; } else { recompute_palette_from_averages(paletteData, nPaletteEntries, avg, count); From 6764650e2a0845466ea6b4aafd5885dcd6a4ea50 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 2 Apr 2021 20:48:06 +1100 Subject: [PATCH 3/4] Reduced memory usage --- src/libImaging/Quant.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index f159737f1..72762232b 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -803,37 +803,35 @@ build_distance_tables( uint32_t i, j; DistanceWithIndex *dwi; - dwi = calloc(nEntries * nEntries, sizeof(DistanceWithIndex)); + for (i = 0; i < nEntries; i++) { + avgDist[i * nEntries + i] = 0; + avgDistSortKey[i * nEntries + i] = &(avgDist[i * nEntries + i]); + for (j = 0; j < i; j++) { + avgDist[j * nEntries + i] = avgDist[i * nEntries + j] = + _DISTSQR(p + i, p + j); + avgDistSortKey[j * nEntries + i] = &(avgDist[j * nEntries + i]); + avgDistSortKey[i * nEntries + j] = &(avgDist[i * nEntries + j]); + } + } + + dwi = calloc(nEntries, sizeof(DistanceWithIndex)); if (!dwi) { return 0; } for (i = 0; i < nEntries; i++) { - avgDist[i * nEntries + i] = 0; - dwi[i * nEntries + i] = (DistanceWithIndex){ - &(avgDist[i * nEntries + i]), - i * nEntries + i - }; - for (j = 0; j < i; j++) { - avgDist[j * nEntries + i] = avgDist[i * nEntries + j] = - _DISTSQR(p + i, p + j); - dwi[j * nEntries + i] = (DistanceWithIndex){ - &(avgDist[j * nEntries + i]), - j * nEntries + i - }; - dwi[i * nEntries + j] = (DistanceWithIndex){ + for (j = 0; j < nEntries; j++) { + dwi[j] = (DistanceWithIndex){ &(avgDist[i * nEntries + j]), - i * nEntries + j + j }; } - } - for (i = 0; i < nEntries; i++) { qsort( - dwi + i * nEntries, + dwi, nEntries, sizeof(DistanceWithIndex), _sort_ulong_ptr_keys); for (j = 0; j < nEntries; j++) { - avgDistSortKey[i * nEntries + j] = dwi[i * nEntries + j].distance; + avgDistSortKey[i * nEntries + j] = dwi[j].distance; } } free(dwi); From a69430047bc1f945677d73b1c888b5e25b052f86 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 2 Apr 2021 21:05:33 +1100 Subject: [PATCH 4/4] Renamed function --- src/libImaging/Quant.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index 72762232b..ea226bcf1 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -759,7 +759,7 @@ typedef struct { } DistanceWithIndex; static int -_sort_ulong_ptr_keys(const void *a, const void *b) { +_distance_index_cmp(const void *a, const void *b) { DistanceWithIndex *A = (DistanceWithIndex *)a; DistanceWithIndex *B = (DistanceWithIndex *)b; if (*A->distance == *B->distance) { @@ -829,7 +829,7 @@ build_distance_tables( dwi, nEntries, sizeof(DistanceWithIndex), - _sort_ulong_ptr_keys); + _distance_index_cmp); for (j = 0; j < nEntries; j++) { avgDistSortKey[i * nEntries + j] = dwi[j].distance; }