-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace git submodule with directory
- Loading branch information
Showing
18 changed files
with
3,897 additions
and
15 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/* | ||
© 2011-2015 by Kornel Lesiński. | ||
This file is part of libimagequant. | ||
libimagequant is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
libimagequant is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with libimagequant. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "libimagequant.h" | ||
#include "pam.h" | ||
#include "blur.h" | ||
|
||
/* | ||
Blurs image horizontally (width 2*size+1) and writes it transposed to dst (called twice gives 2d blur) | ||
*/ | ||
static void transposing_1d_blur(unsigned char *restrict src, unsigned char *restrict dst, unsigned int width, unsigned int height, const unsigned int size) | ||
{ | ||
assert(size > 0); | ||
|
||
for(unsigned int j=0; j < height; j++) { | ||
unsigned char *restrict row = src + j*width; | ||
|
||
// accumulate sum for pixels outside line | ||
unsigned int sum; | ||
sum = row[0]*size; | ||
for(unsigned int i=0; i < size; i++) { | ||
sum += row[i]; | ||
} | ||
|
||
// blur with left side outside line | ||
for(unsigned int i=0; i < size; i++) { | ||
sum -= row[0]; | ||
sum += row[i+size]; | ||
|
||
dst[i*height + j] = sum / (size*2); | ||
} | ||
|
||
for(unsigned int i=size; i < width-size; i++) { | ||
sum -= row[i-size]; | ||
sum += row[i+size]; | ||
|
||
dst[i*height + j] = sum / (size*2); | ||
} | ||
|
||
// blur with right side outside line | ||
for(unsigned int i=width-size; i < width; i++) { | ||
sum -= row[i-size]; | ||
sum += row[width-1]; | ||
|
||
dst[i*height + j] = sum / (size*2); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Picks maximum of neighboring pixels (blur + lighten) | ||
*/ | ||
LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height) | ||
{ | ||
for(unsigned int j=0; j < height; j++) { | ||
const unsigned char *row = src + j*width, | ||
*prevrow = src + (j > 1 ? j-1 : 0)*width, | ||
*nextrow = src + MIN(height-1,j+1)*width; | ||
|
||
unsigned char prev,curr=row[0],next=row[0]; | ||
|
||
for(unsigned int i=0; i < width-1; i++) { | ||
prev=curr; | ||
curr=next; | ||
next=row[i+1]; | ||
|
||
unsigned char t1 = MAX(prev,next); | ||
unsigned char t2 = MAX(nextrow[i],prevrow[i]); | ||
*dst++ = MAX(curr,MAX(t1,t2)); | ||
} | ||
unsigned char t1 = MAX(curr,next); | ||
unsigned char t2 = MAX(nextrow[width-1],prevrow[width-1]); | ||
*dst++ = MAX(t1,t2); | ||
} | ||
} | ||
|
||
/** | ||
* Picks minimum of neighboring pixels (blur + darken) | ||
*/ | ||
LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height) | ||
{ | ||
for(unsigned int j=0; j < height; j++) { | ||
const unsigned char *row = src + j*width, | ||
*prevrow = src + (j > 1 ? j-1 : 0)*width, | ||
*nextrow = src + MIN(height-1,j+1)*width; | ||
|
||
unsigned char prev,curr=row[0],next=row[0]; | ||
|
||
for(unsigned int i=0; i < width-1; i++) { | ||
prev=curr; | ||
curr=next; | ||
next=row[i+1]; | ||
|
||
unsigned char t1 = MIN(prev,next); | ||
unsigned char t2 = MIN(nextrow[i],prevrow[i]); | ||
*dst++ = MIN(curr,MIN(t1,t2)); | ||
} | ||
unsigned char t1 = MIN(curr,next); | ||
unsigned char t2 = MIN(nextrow[width-1],prevrow[width-1]); | ||
*dst++ = MIN(t1,t2); | ||
} | ||
} | ||
|
||
/* | ||
Filters src image and saves it to dst, overwriting tmp in the process. | ||
Image must be width*height pixels high. Size controls radius of box blur. | ||
*/ | ||
LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size) | ||
{ | ||
assert(size > 0); | ||
if (width < 2*size+1 || height < 2*size+1) { | ||
return; | ||
} | ||
transposing_1d_blur(src, tmp, width, height, size); | ||
transposing_1d_blur(tmp, dst, height, width, size); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size); | ||
LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height); | ||
LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
** © 2011-2016 by Kornel Lesiński. | ||
** See COPYRIGHT file for license. | ||
*/ | ||
|
||
#include "libimagequant.h" | ||
#include "pam.h" | ||
#include "kmeans.h" | ||
#include "nearest.h" | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#ifdef _OPENMP | ||
#include <omp.h> | ||
#else | ||
#define omp_get_max_threads() 1 | ||
#define omp_get_thread_num() 0 | ||
#endif | ||
|
||
/* | ||
* K-Means iteration: new palette color is computed from weighted average of colors that map to that palette entry. | ||
*/ | ||
LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state average_color[]) | ||
{ | ||
memset(average_color, 0, sizeof(average_color[0])*(KMEANS_CACHE_LINE_GAP+map->colors)*max_threads); | ||
} | ||
|
||
LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[]) | ||
{ | ||
match += thread * (KMEANS_CACHE_LINE_GAP+map->colors); | ||
average_color[match].a += acolor.a * value; | ||
average_color[match].r += acolor.r * value; | ||
average_color[match].g += acolor.g * value; | ||
average_color[match].b += acolor.b * value; | ||
average_color[match].total += value; | ||
} | ||
|
||
LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state average_color[]) | ||
{ | ||
for (unsigned int i=0; i < map->colors; i++) { | ||
double a=0, r=0, g=0, b=0, total=0; | ||
|
||
// Aggregate results from all threads | ||
for(unsigned int t=0; t < max_threads; t++) { | ||
const unsigned int offset = (KMEANS_CACHE_LINE_GAP+map->colors) * t + i; | ||
|
||
a += average_color[offset].a; | ||
r += average_color[offset].r; | ||
g += average_color[offset].g; | ||
b += average_color[offset].b; | ||
total += average_color[offset].total; | ||
} | ||
|
||
if (total && !map->palette[i].fixed) { | ||
map->palette[i].acolor = (f_pixel){ | ||
.a = a / total, | ||
.r = r / total, | ||
.g = g / total, | ||
.b = b / total, | ||
}; | ||
map->palette[i].popularity = total; | ||
} | ||
} | ||
} | ||
|
||
LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback) | ||
{ | ||
const unsigned int max_threads = omp_get_max_threads(); | ||
LIQ_ARRAY(kmeans_state, average_color, (KMEANS_CACHE_LINE_GAP+map->colors) * max_threads); | ||
kmeans_init(map, max_threads, average_color); | ||
struct nearest_map *const n = nearest_init(map); | ||
hist_item *const achv = hist->achv; | ||
const int hist_size = hist->size; | ||
|
||
double total_diff=0; | ||
#if __GNUC__ >= 9 | ||
#pragma omp parallel for if (hist_size > 2000) \ | ||
schedule(static) default(none) shared(achv,average_color,callback,hist_size,map,n) reduction(+:total_diff) | ||
#else | ||
#pragma omp parallel for if (hist_size > 2000) \ | ||
schedule(static) default(none) shared(average_color,callback) reduction(+:total_diff) | ||
#endif | ||
for(int j=0; j < hist_size; j++) { | ||
float diff; | ||
unsigned int match = nearest_search(n, &achv[j].acolor, achv[j].tmp.likely_colormap_index, &diff); | ||
achv[j].tmp.likely_colormap_index = match; | ||
total_diff += diff * achv[j].perceptual_weight; | ||
|
||
kmeans_update_color(achv[j].acolor, achv[j].perceptual_weight, map, match, omp_get_thread_num(), average_color); | ||
|
||
if (callback) callback(&achv[j], diff); | ||
} | ||
|
||
nearest_free(n); | ||
kmeans_finalize(map, max_threads, average_color); | ||
|
||
return total_diff / hist->total_perceptual_weight; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
|
||
#ifndef KMEANS_H | ||
#define KMEANS_H | ||
|
||
// Spread memory touched by different threads at least 64B apart which I assume is the cache line size. This should avoid memory write contention. | ||
#define KMEANS_CACHE_LINE_GAP ((64+sizeof(kmeans_state)-1)/sizeof(kmeans_state)) | ||
|
||
typedef struct { | ||
double a, r, g, b, total; | ||
} kmeans_state; | ||
|
||
typedef void (*kmeans_callback)(hist_item *item, float diff); | ||
|
||
LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state state[]); | ||
LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[]); | ||
LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state state[]); | ||
LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback); | ||
|
||
#endif |
Submodule libimagequant
deleted from
cc282b
Oops, something went wrong.