diff --git a/INSTALL.md b/INSTALL.md index a855e9cc..1c2f8f58 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -34,6 +34,7 @@ You can also use [Meson](https://mesonbuild.com/) as an alternative. * libstartup-notification-1.0 * libxkbcommon >= 0.4.1 * libxkbcommon-x11 +* libjpeg * libxcb (sometimes split, you need libxcb, libxcb-xkb and libxcb-randr libxcb-xinerama) * xcb-util * xcb-util-wm (sometimes split as libxcb-ewmh and libxcb-icccm) diff --git a/Makefile.am b/Makefile.am index 481b4c85..97a6f387 100644 --- a/Makefile.am +++ b/Makefile.am @@ -165,6 +165,7 @@ rofi_LDADD=\ $(pango_LIBS)\ $(cairo_LIBS)\ $(librsvg_LIBS)\ + $(libjpeg_LIBS)\ $(LIBS) ## @@ -321,6 +322,7 @@ textbox_test_CFLAGS=\ $(cairo_CFLAGS)\ $(libsn_CFLAGS)\ $(librsvg_CFLAGS)\ + $(libjpeg_CFLAGS)\ -DPLUGIN_PATH=\"${libdir}/rofi\"\ -DTHEME_DIR=\"$(themedir)\"\ -I$(top_srcdir)/include/\ diff --git a/configure.ac b/configure.ac index 349508a0..9526b291 100644 --- a/configure.ac +++ b/configure.ac @@ -128,6 +128,7 @@ PKG_CHECK_MODULES([pango], [pango pangocairo]) PKG_CHECK_MODULES([cairo], [cairo cairo-xcb]) PKG_CHECK_MODULES([libsn], [libstartup-notification-1.0 ]) PKG_CHECK_MODULES([librsvg], [librsvg-2.0 ]) +PKG_CHECK_MODULES([libjpeg], [libjpeg]) dnl --------------------------------------------------------------------- dnl check - Unit testing. diff --git a/source/rofi-icon-fetcher.c b/source/rofi-icon-fetcher.c index e67e2f24..e0917248 100644 --- a/source/rofi-icon-fetcher.c +++ b/source/rofi-icon-fetcher.c @@ -39,6 +39,9 @@ #include "nkutils-xdg-theme.h" +#include +#include + typedef struct { // Context for icon-themes. @@ -129,6 +132,67 @@ void rofi_icon_fetcher_destroy ( void ) g_free ( rofi_icon_fetcher_data ); } + + + +static cairo_surface_t* cairo_image_surface_create_from_jpeg_private(struct jpeg_decompress_struct* cinfo) { + cairo_surface_t* surface = 0; + unsigned char* data = 0; + unsigned char* rgb = 0; + + jpeg_read_header(cinfo, TRUE); + jpeg_start_decompress(cinfo); + + surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, cinfo->image_width, cinfo->image_height); + data = cairo_image_surface_get_data(surface); + rgb = (unsigned char*)(malloc(cinfo->output_width * cinfo->output_components)); + + while(cinfo->output_scanline < cinfo->output_height) { + unsigned int i; + int scanline = cinfo->output_scanline * cairo_image_surface_get_stride(surface); + + jpeg_read_scanlines(cinfo, &rgb, 1); + + for(i = 0; i < cinfo->output_width; i++) { + int offset = scanline + (i * 4); + + data[offset + 3] = 255; + data[offset + 2] = rgb[(i * 3)]; + data[offset + 1] = rgb[(i * 3) + 1]; + data[offset ] = rgb[(i * 3) + 2]; + } + } + + free(rgb); + + jpeg_finish_decompress(cinfo); + jpeg_destroy_decompress(cinfo); + + cairo_surface_mark_dirty(surface); + + return surface; +} + +static cairo_surface_t* cairo_image_surface_create_from_jpeg(const char* file) { + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + cairo_surface_t* surface; + FILE* infile; + + if((infile = fopen(file, "rb")) == NULL) return NULL; + + cinfo.err = jpeg_std_error(&jerr); + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + + surface = cairo_image_surface_create_from_jpeg_private(&cinfo); + + fclose(infile); + + return surface; +} + static void rofi_icon_fetcher_worker ( thread_state *sdata, G_GNUC_UNUSED gpointer user_data ) { g_debug ( "starting up icon fetching thread." ); @@ -160,6 +224,9 @@ static void rofi_icon_fetcher_worker ( thread_state *sdata, G_GNUC_UNUSED gpoint if ( g_str_has_suffix ( icon_path, ".png" ) ) { icon_surf = cairo_image_surface_create_from_png ( icon_path ); } + else if ( g_str_has_suffix ( icon_path, ".jpeg" ) || g_str_has_suffix ( icon_path, ".jpg" ) ) { + icon_surf = cairo_image_surface_create_from_jpeg ( icon_path ); + } else if ( g_str_has_suffix ( icon_path, ".svg" ) ) { icon_surf = cairo_image_surface_create_from_svg ( icon_path, sentry->size ); } @@ -167,6 +234,30 @@ static void rofi_icon_fetcher_worker ( thread_state *sdata, G_GNUC_UNUSED gpoint g_debug ( "icon type not yet supported: %s", icon_path ); } if ( icon_surf ) { + if ( cairo_surface_status ( icon_surf ) == CAIRO_STATUS_SUCCESS ) { + float sw = sentry->size/(float)cairo_image_surface_get_width( icon_surf ); + float sh = sentry->size/(float)cairo_image_surface_get_height( icon_surf ); + + float scale = ( sw > sh)? sh:sw; + if ( scale < 0.5 ) + { + cairo_surface_t * surface = cairo_image_surface_create( + cairo_image_surface_get_format( icon_surf ), + cairo_image_surface_get_width( icon_surf )*scale, + cairo_image_surface_get_height( icon_surf )*scale); + + cairo_t *d = cairo_create ( surface ); + cairo_scale ( d, scale, scale ); + cairo_set_source_surface ( d, icon_surf, 0.0,0.0); + cairo_paint ( d ); + + cairo_destroy ( d ); + cairo_surface_destroy ( icon_surf ); + icon_surf = surface; + } + + + } // check if surface is valid. if ( cairo_surface_status ( icon_surf ) != CAIRO_STATUS_SUCCESS ) { g_debug ( "icon failed to open: %s(%d): %s", sentry->entry->name, sentry->size, icon_path );