Skip to content

Commit

Permalink
Replace git submodule with directory
Browse files Browse the repository at this point in the history
  • Loading branch information
larrabee committed Jun 7, 2019
1 parent 46fbc86 commit 150c56b
Show file tree
Hide file tree
Showing 18 changed files with 3,897 additions and 15 deletions.
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

11 changes: 2 additions & 9 deletions Attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,8 @@ import (
)

/*
#cgo CFLAGS: -I${SRCDIR}/libimagequant -std=c99
#cgo LDFLAGS: -lm
#include "blur.c"
#include "kmeans.c"
#include "libimagequant.c"
#include "mediancut.c"
#include "mempool.c"
#include "nearest.c"
#include "pam.c"
#cgo CFLAGS: -O3 -fopenmp -fomit-frame-pointer -Wall -Wno-attributes -std=c99 -DNDEBUG -DUSE_SSE=1 -msse
#cgo LDFLAGS: -lm -fopenmp -ldl
#include "libimagequant.h"
*/
import "C"
Expand Down
4 changes: 2 additions & 2 deletions COPYRIGHT
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
libimagequant is derived from code by Jef Poskanzer and Greg Roelofs
licensed under pngquant's original license (at the end of this file),
and contains extensive changes and additions by Kornel Lesiński
licensed under GPL v3.
licensed under GPL v3 or later.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

libimagequant © 2009-2016 by Kornel Lesiński.
libimagequant © 2009-2018 by Kornel Lesiński.

GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Expand Down
132 changes: 132 additions & 0 deletions blur.c
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);
}
4 changes: 4 additions & 0 deletions blur.h
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);
98 changes: 98 additions & 0 deletions kmeans.c
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;
}
19 changes: 19 additions & 0 deletions kmeans.h
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
1 change: 0 additions & 1 deletion libimagequant
Submodule libimagequant deleted from cc282b
Loading

0 comments on commit 150c56b

Please sign in to comment.