add support for JPEG images

This commit is contained in:
William Osler 2017-12-07 21:09:20 -08:00
parent 4318dbe051
commit 8678e0ad67
No known key found for this signature in database
GPG Key ID: DEF12F2C6161EB8D
6 changed files with 166 additions and 4 deletions

View File

@ -22,7 +22,8 @@ i3lock_CFLAGS = \
$(XKBCOMMON_CFLAGS) \
$(CAIRO_CFLAGS) \
$(CODE_COVERAGE_CFLAGS) \
$(X11_CFLAGS)
$(X11_CFLAGS) \
$(JPEG_CFLAGS)
i3lock_CPPFLAGS = \
$(AM_CPPFLAGS) \
@ -35,7 +36,8 @@ i3lock_LDADD = \
$(XKBCOMMON_LIBS) \
$(CAIRO_LIBS) \
$(CODE_COVERAGE_LDFLAGS) \
$(X11_LIBS)
$(X11_LIBS) \
$(JPEG_LIBS)
i3lock_SOURCES = \
cursors.h \
@ -51,7 +53,9 @@ i3lock_SOURCES = \
tinyexpr.h \
blur_simd.c \
blur.c \
blur.h
blur.h \
jpg.c \
jpg.h
EXTRA_DIST = \

View File

@ -90,6 +90,7 @@ i3lock now uses GNU autotools for building; you'll need to do something like `au
- libx11-xcb-dev
- libxkbcommon >= 0.5.0
- libxkbcommon-x11 >= 0.5.0
- libjpeg-turbo >= 1.4.90
(On centos/RHEL/etc, the packages tend to look like `cairo-devel` instead of `libcairo-dev`. Use `yum provides \*/<header.h>` to figure out what packages you need.)
@ -97,6 +98,8 @@ i3lock now uses GNU autotools for building; you'll need to do something like `au
sudo apt-get install pkg-config libxcb1 libpam-dev libcairo-dev libxcb-composite0 libxcb-composite0-dev libxcb-xinerama0-dev libev-dev libx11-dev libx11-xcb-dev libxkbcommon0 libxkbcommon-x11-0 libxcb-dpms0-dev libxcb-image0-dev libxcb-util0-dev libxcb-xkb-dev libxkbcommon-x11-dev libxkbcommon-dev
For JPEG lock image support, you'll also need `libjpeg-turbo8` version 1.4.90 or newer (Ubuntu 17.04 or later)
##### Aur Package
[Stable](https://aur.archlinux.org/packages/i3lock-color/)

View File

@ -88,6 +88,7 @@ PKG_CHECK_MODULES([XCB_UTIL], [xcb-event xcb-util xcb-atom])
PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon xkbcommon-x11])
PKG_CHECK_MODULES([CAIRO], [cairo])
PKG_CHECK_MODULES([X11], [x11])
PKG_CHECK_MODULES([JPEG], [libjpeg])
# Checks for programs.

View File

@ -20,6 +20,7 @@
#include <xcb/xcb.h>
#include <xcb/xkb.h>
#include <err.h>
#include <errno.h>
#include <assert.h>
#ifdef __OpenBSD__
#include <bsd_auth.h>
@ -50,6 +51,7 @@
#include "unlock_indicator.h"
#include "randr.h"
#include "blur.h"
#include "jpg.h"
#define TSTAMP_N_SECS(n) (n * 1.0)
#define TSTAMP_N_MINS(n) (60 * TSTAMP_N_SECS(n))
@ -988,6 +990,7 @@ int main(int argc, char *argv[]) {
struct passwd *pw;
char *username;
char *image_path = NULL;
JPEG_INFO jpg_info;
#ifndef __OpenBSD__
int ret;
struct pam_conv conv = {conv_callback, NULL};
@ -1604,7 +1607,20 @@ int main(int argc, char *argv[]) {
if (image_path) {
/* Create a pixmap to render on, fill it with the background color */
img = cairo_image_surface_create_from_png(image_path);
if (file_is_jpg(image_path)) {
if (debug_mode) {
fprintf(stderr, "Image looks like a jpeg, decoding\n");
}
unsigned char* jpg_data = read_JPEG_file(image_path, &jpg_info);
if (jpg_data != NULL) {
img = cairo_image_surface_create_for_data(jpg_data,
CAIRO_FORMAT_ARGB32, jpg_info.width, jpg_info.height,
jpg_info.stride);
}
} else {
img = cairo_image_surface_create_from_png(image_path);
}
/* In case loading failed, we just pretend no -i was specified. */
if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "Could not load image \"%s\": %s\n",

117
jpg.c Normal file
View File

@ -0,0 +1,117 @@
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <err.h>
#include <errno.h>
#include <cairo.h>
#include <jpeglib.h>
#include "jpg.h"
/*
* Checks if the file is a JPEG by looking for a valid JPEG header.
*/
bool file_is_jpg(char* file_path) {
FILE* image_file;
uint16_t file_header;
size_t read_count;
// TODO: Consider endianess on non-x86 platforms
uint16_t jpg_magick = 0xd8ff;
image_file = fopen(file_path, "rb");
if (image_file == NULL) {
int img_err = errno;
fprintf(stderr, "Could not open image file %s: %s\n",
file_path, strerror(img_err));
return false;
}
read_count = fread(&file_header, sizeof(file_header), 1, image_file);
fclose(image_file);
if (read_count < 1) {
fprintf(stderr, "Error searching for JPEG header in %s\n", file_path);
return false;
}
return file_header == jpg_magick;
}
/*
* Reads a JPEG from a file into memory, in a format that Cairo can create a
* surface from.
*/
void* read_JPEG_file(char *file_path, JPEG_INFO *jpg_info) {
int img_err;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *infile; /* source file */
void *img; /* decompressed image data pointer */
if ((infile = fopen(file_path, "rb")) == NULL) {
img_err = errno;
fprintf(stderr, "Could not open image file %s: %s\n",
file_path, strerror(img_err));
return NULL;
}
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
(void) jpeg_read_header(&cinfo, TRUE);
// BGRA + endianness change = RGBA?
// TODO: Test this code on non-x86_64 platforms
cinfo.out_color_space = JCS_EXT_BGRA;
(void) jpeg_start_decompress(&cinfo);
jpg_info->height = cinfo.output_height;
jpg_info->width = cinfo.output_width;
/* Get the *cairo* stride rather than the stride from the image. This is
* the space needed in memory for each row for optimized Cairo rendering. */
int cairo_stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
cinfo.output_width);
jpg_info->stride = cairo_stride;
if (cairo_stride < jpg_info->width) {
/* This should never happen, but if it does then the following code
* will potentially write into unallocated memory */
fprintf(
stderr,
"WARNING: Cairo stride shorter than JPEG width. Aborting JPEG read."
);
return NULL;
}
// Allocate storage for the final, decompressed image.
img = calloc(cairo_stride, cinfo.output_height);
if (img == NULL) {
fprintf(stderr, "Could not allocate memory for JPEG decode\n");
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return NULL;
}
while (cinfo.output_scanline < cinfo.output_height) {
/* Normally, you would allocate a buffer using libJPEG's memory
* management and write into it, but since we're reading one row at a
* time, we just write it directly into the image memory space */
unsigned char* pos = img + (cairo_stride * (cinfo.output_scanline));
(void) jpeg_read_scanlines(&cinfo, &pos, 1);
}
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return img;
}

21
jpg.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef _JPG_H
#define _JPG_H
typedef struct {
uint height;
uint width;
uint stride; // The width of each row in memory, in bytes
} JPEG_INFO;
/*
* Checks if the file is a JPEG by looking for a valid JPEG header.
*/
bool file_is_jpg(char* file_path);
/*
* Reads a JPEG from a file into memory, in a format that Cairo can create a
* surface from.
*/
void* read_JPEG_file(char *filename, JPEG_INFO *jpg_info);
#endif