#include <gtk/gtk.h>
#include <glib.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <dirent.h>
#include <queue>
/**
* gnome_thumbnail_scale_down_pixbuf:
* @pixbuf: a #GdkPixbuf
* @dest_width: the desired new width
* @dest_height: the desired new height
*
* Scales the pixbuf to the desired size. This function
* is a lot faster than gdk-pixbuf when scaling down by
* large amounts.
*
* Return value: a scaled pixbuf
*
* Since: 2.2
**/
GdkPixbuf *
gnome_desktop_thumbnail_scale_down_pixbuf (GdkPixbuf *pixbuf,
int dest_width,
int dest_height)
{
int source_width, source_height;
int s_x1, s_y1, s_x2, s_y2;
int s_xfrac, s_yfrac;
int dx, dx_frac, dy, dy_frac;
div_t ddx, ddy;
int x, y;
int r, g, b, a;
int n_pixels;
gboolean has_alpha;
guchar *dest, *src, *xsrc, *src_pixels;
GdkPixbuf *dest_pixbuf;
int pixel_stride;
int source_rowstride, dest_rowstride;
if (dest_width == 0 || dest_height == 0) {
return NULL;
}
source_width = gdk_pixbuf_get_width (pixbuf);
source_height = gdk_pixbuf_get_height (pixbuf);
g_assert (source_width >= dest_width);
g_assert (source_height >= dest_height);
ddx = div (source_width, dest_width);
dx = ddx.quot;
dx_frac = ddx.rem;
ddy = div (source_height, dest_height);
dy = ddy.quot;
dy_frac = ddy.rem;
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
source_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
src_pixels = gdk_pixbuf_get_pixels (pixbuf);
dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8,
dest_width, dest_height);
dest = gdk_pixbuf_get_pixels (dest_pixbuf);
dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf);
pixel_stride = (has_alpha)?4:3;
s_y1 = 0;
s_yfrac = -dest_height/2;
while (s_y1 < source_height) {
s_y2 = s_y1 + dy;
s_yfrac += dy_frac;
if (s_yfrac > 0) {
s_y2++;
s_yfrac -= dest_height;
}
s_x1 = 0;
s_xfrac = -dest_width/2;
while (s_x1 < source_width) {
s_x2 = s_x1 + dx;
s_xfrac += dx_frac;
if (s_xfrac > 0) {
s_x2++;
s_xfrac -= dest_width;
}
/* Average block of [x1,x2[ x [y1,y2[ and store in dest */
r = g = b = a = 0;
n_pixels = 0;
src = src_pixels + s_y1 * source_rowstride + s_x1 * pixel_stride;
for (y = s_y1; y < s_y2; y++) {
xsrc = src;
if (has_alpha) {
for (x = 0; x < s_x2-s_x1; x++) {
n_pixels++;
r += xsrc[3] * xsrc[0];
g += xsrc[3] * xsrc[1];
b += xsrc[3] * xsrc[2];
a += xsrc[3];
xsrc += 4;
}
} else {
for (x = 0; x < s_x2-s_x1; x++) {
n_pixels++;
r += *xsrc++;
g += *xsrc++;
b += *xsrc++;
}
}
src += source_rowstride;
}
if (has_alpha) {
if (a != 0) {
*dest++ = r / a;
*dest++ = g / a;
*dest++ = b / a;
*dest++ = a / n_pixels;
} else {
*dest++ = 0;
*dest++ = 0;
*dest++ = 0;
*dest++ = 0;
}
} else {
*dest++ = r / n_pixels;
*dest++ = g / n_pixels;
*dest++ = b / n_pixels;
}
s_x1 = s_x2;
}
s_y1 = s_y2;
dest += dest_rowstride - dest_width * pixel_stride;
}
return dest_pixbuf;
}
static GdkPixbuf *
scale_pixbuf_preserve_aspect_ratio (GdkPixbuf *pixbuf,
gint size,
GdkInterpType interp)
{
GdkPixbuf *new_icon;
gdouble w, h, new_width, new_height, max_edge;
w = gdk_pixbuf_get_width (pixbuf);
h = gdk_pixbuf_get_height (pixbuf);
max_edge = MAX (w, h);
new_width = size * (w / max_edge);
new_height = size * (h / max_edge);
/* Scale the image down, preserving the aspect ratio */
new_icon = gdk_pixbuf_scale_simple (pixbuf, (gint)new_width, (gint)new_height, interp);
//new_icon = gnome_desktop_thumbnail_scale_down_pixbuf(pixbuf, (gint)new_width, (gint)new_height);
return new_icon;
}
static int global_sequence[20] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
static GMutex *global_sequence_mutex = NULL;
std::queue<std::string> imageQueue;
void init_give_me_next_number ()
{
g_assert (global_sequence_mutex == NULL);
global_sequence_mutex = g_mutex_new ();
}
int give_me_next_number ()
{
static int current_number = 0;
int ret_val;
g_mutex_lock (global_sequence_mutex);
ret_val = global_sequence[current_number];
current_number++;
g_mutex_unlock (global_sequence_mutex);
return ret_val;
}
static void* say_hello(gpointer data)
{
g_print ("Hello World! It's me, thread #%d using sequence number %d!\n",
GPOINTER_TO_INT (data), give_me_next_number ());
int thread_nr = GPOINTER_TO_INT (data);
int errors = 0;
while (!imageQueue.empty())
{
std::string imageName = imageQueue.front();
// Lock for popping of queue
g_mutex_lock (global_sequence_mutex);
imageQueue.pop();
g_mutex_unlock (global_sequence_mutex);
// unlock
GError *gerror = NULL;
GdkPixbuf *originalpb;
std::string imagePath = "/home/mark/ThumbnailingBenchmarks/2000_Wallpapers-Reanimation/" + imageName;
std::string thumbnailPath = "/home/mark/ThumbnailingBenchmarks/thumbnails/GDK_INTERP_BILINEAR/" + imageName + ".png";
originalpb = gdk_pixbuf_new_from_file_at_scale(imagePath.c_str(), 200, 200, true, &gerror);
if (!originalpb)
{
printf("error message: %s\n", gerror->message);
errors++;
gdk_pixbuf_unref(originalpb);
continue;
//exit(1);
}
gdk_pixbuf_save (originalpb, thumbnailPath.c_str(), "png", &gerror, NULL);
gdk_pixbuf_unref(originalpb);
printf("Thread # %d <> %s\n", thread_nr, imageName.c_str());
}
printf("\n\nThere where %i errors! in thread: %d \n", errors, thread_nr);
g_thread_exit (NULL);
}
void listdir(const char *path)
{
// first off, we need to create a pointer to a directory
DIR *pdir = NULL; // remember, it's good practice to initialise a pointer to NULL!
pdir = opendir (path); // "." will refer to the current directory
struct dirent *pent = NULL;
if (pdir == NULL) // if pdir wasn't initialised correctly
{
// print an error message and exit the program
printf ("\nERROR! pdir could not be initialised correctly");
return; // exit the function
} // end if
while ((pent = readdir (pdir))) // while there is still something in the directory to list
{
if (pent == NULL) // if pent has not been initialised correctly
{
// print an error message, and exit the program
printf ("\nERROR! pent could not be initialised correctly");
return; // exit the function
}
if (strcmp(".", pent->d_name) == 0 || strcmp("..", pent->d_name) == 0)
continue;
// otherwise, it was initialised correctly. let's print it on the console:
imageQueue.push(pent->d_name);
}
// finally, let's close the directory
closedir (pdir);
}
void GLibThumbnailingBenchmark()
{
int errors = 0;
while (!imageQueue.empty())
{
GError *gerror = NULL;
GdkPixbuf *originalpb, *thumbpixbuff;
std::string imageName = imageQueue.front();
imageQueue.pop();
std::string imagePath = "/home/mark/ThumbnailingBenchmarks/2000_Wallpapers-Reanimation/" + imageName;
std::string thumbnailPath = "/home/mark/ThumbnailingBenchmarks/thumbnails/GDK_INTERP_BILINEAR/" + imageName + ".png";
originalpb = gdk_pixbuf_new_from_file(imagePath.c_str(), &gerror);
if (!originalpb)
{
printf("error message: %s\n", gerror->message);
errors++;
gdk_pixbuf_unref(originalpb);
gdk_pixbuf_unref(thumbpixbuff);
continue;
//exit(1);
}
thumbpixbuff = scale_pixbuf_preserve_aspect_ratio(originalpb, 200, GDK_INTERP_BILINEAR);
gdk_pixbuf_save (originalpb, thumbnailPath.c_str(), "png", &gerror, NULL);
gdk_pixbuf_unref(thumbpixbuff);
gdk_pixbuf_unref(originalpb);
printf("<> %s\n", imageName.c_str());
}
printf("\n\nThere where %i errors!\n", errors);
}
void GLibThumbnailingBenchmarkRapid()
{
int errors = 0;
while (!imageQueue.empty())
{
GError *gerror = NULL;
GdkPixbuf *originalpb;
std::string imageName = imageQueue.front();
imageQueue.pop();
std::string imagePath = "/home/mark/ThumbnailingBenchmarks/2000_Wallpapers-Reanimation/" + imageName;
std::string thumbnailPath = "/home/mark/ThumbnailingBenchmarks/thumbnails/GDK_INTERP_BILINEAR/" + imageName + ".png";
originalpb = gdk_pixbuf_new_from_file_at_scale(imagePath.c_str(), 200, 200, true, &gerror);
if (!originalpb)
{
printf("error message: %s\n", gerror->message);
errors++;
gdk_pixbuf_unref(originalpb);
continue;
//exit(1);
}
gdk_pixbuf_save (originalpb, thumbnailPath.c_str(), "png", &gerror, NULL);
gdk_pixbuf_unref(originalpb);
printf("<> %s\n", imageName.c_str());
}
printf("\n\nThere where %i errors!\n", errors);
}
void GlibThreadedThumbnailingBenchmark()
{
// Setup threads
static int numOfThreads = 10;
GThread *threads[numOfThreads];
GError *error = NULL;
if (!g_thread_supported ())
g_thread_init (NULL);
init_give_me_next_number ();
int i;
for (i = 0; i < numOfThreads; i++)
{
g_print ("creating thread %d\n", i);
threads[i] = g_thread_create (say_hello, GINT_TO_POINTER (i), TRUE, &error);
if (error)
{
g_print ("Error creating thread %d, exiting\n");
g_error_free (error);
exit (1);
}
}
g_thread_exit (NULL);
exit(1);
}
int main (int argc, char *argv[])
{
gtk_set_locale ();
gtk_init (NULL, NULL);
// This fills the imageQueue queue
listdir("/home/mark/ThumbnailingBenchmarks/2000_Wallpapers-Reanimation");
// GLib Thumbnailing Benchmark
//GLibThumbnailingBenchmark();
// Glib more rapid thumbnailing benchmark
//GLibThumbnailingBenchmarkRapid();
//Glib threaded thumbnailing (this is the GLibThumbnailingBenchmarkRapid benchmark only in multiple threads)
GlibThreadedThumbnailingBenchmark();
return 0;
}