Compare commits

...

4 Commits

Author SHA1 Message Date
c18404efee
Add basic print functionality 2021-12-15 04:44:34 +01:00
bff2b92c9e
Clean up 2021-12-15 03:53:13 +01:00
7297c40f93
Employ libwebp's alpha premultiplication
It seems to perform roughly equally in optimized builds.
2021-12-15 03:43:57 +01:00
ea2d159773
Clean up dependencies 2021-12-15 03:29:47 +01:00
3 changed files with 121 additions and 53 deletions

View File

@ -162,6 +162,9 @@ try_append_page(cairo_surface_t *surface, cairo_surface_t **result,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// From libwebp, verified to exactly match [x * a / 255].
#define PREMULTIPLY8(a, x) (((uint32_t) (x) * (uint32_t) (a) * 32897U) >> 23)
static bool
pull_passthrough(const wuffs_base__more_information *minfo,
wuffs_base__io_buffer *src, wuffs_base__io_buffer *dst, GError **error)
@ -1375,12 +1378,11 @@ load_libheif_image(struct heif_image_handle *handle, GError **error)
for (int y = 0; y < h; y++) {
uint32_t *dstp = (uint32_t *) (dst + dst_stride * y);
for (int x = 0; x < w; x++) {
uint32_t pixel = dstp[x], a = pixel >> 24;
uint8_t r = pixel >> 16;
uint8_t g = pixel >> 8;
uint8_t b = pixel;
uint32_t argb = dstp[x], a = argb >> 24;
dstp[x] = a << 24 |
(r * a / 255) << 16 | (g * a / 255) << 8 | (b * a / 255);
PREMULTIPLY8(a, 0xFF & (argb >> 16)) << 16 |
PREMULTIPLY8(a, 0xFF & (argb >> 8)) << 8 |
PREMULTIPLY8(a, 0xFF & argb);
}
}
}
@ -2361,9 +2363,9 @@ read_spng_thumbnail(
for (size_t i = size / sizeof *data; i--; ) {
const uint8_t *unit = (const uint8_t *) &data[i];
uint32_t a = unit[3],
b = unit[2] * a / 255,
g = unit[1] * a / 255,
r = unit[0] * a / 255;
b = PREMULTIPLY8(a, unit[2]),
g = PREMULTIPLY8(a, unit[1]),
r = PREMULTIPLY8(a, unit[0]);
data[i] = a << 24 | r << 16 | g << 8 | b;
}
} else {

View File

@ -165,6 +165,46 @@ get_display_dimensions(FastivView *self, int *width, int *height)
*height = ceil(h * self->scale);
}
static cairo_matrix_t
get_orientation_matrix(FastivIoOrientation o, double width, double height)
{
cairo_matrix_t matrix = {};
cairo_matrix_init_identity(&matrix);
switch (o) {
case FastivIoOrientation90:
cairo_matrix_rotate(&matrix, -M_PI_2);
cairo_matrix_translate(&matrix, -width, 0);
break;
case FastivIoOrientation180:
cairo_matrix_scale(&matrix, -1, -1);
cairo_matrix_translate(&matrix, -width, -height);
break;
case FastivIoOrientation270:
cairo_matrix_rotate(&matrix, +M_PI_2);
cairo_matrix_translate(&matrix, 0, -height);
break;
case FastivIoOrientationMirror0:
cairo_matrix_scale(&matrix, -1, +1);
cairo_matrix_translate(&matrix, -width, 0);
break;
case FastivIoOrientationMirror90:
cairo_matrix_rotate(&matrix, +M_PI_2);
cairo_matrix_scale(&matrix, -1, +1);
cairo_matrix_translate(&matrix, -width, -height);
break;
case FastivIoOrientationMirror180:
cairo_matrix_scale(&matrix, +1, -1);
cairo_matrix_translate(&matrix, 0, -height);
break;
case FastivIoOrientationMirror270:
cairo_matrix_rotate(&matrix, -M_PI_2);
cairo_matrix_scale(&matrix, -1, +1);
default:
break;
}
return matrix;
}
static void
fastiv_view_get_preferred_height(
GtkWidget *widget, gint *minimum, gint *natural)
@ -320,41 +360,7 @@ fastiv_view_draw(GtkWidget *widget, cairo_t *cr)
cairo_scale(cr, self->scale, self->scale);
cairo_set_source_surface(cr, self->frame, 0, 0);
cairo_matrix_t matrix = {};
cairo_matrix_init_identity(&matrix);
switch (self->orientation) {
case FastivIoOrientation90:
cairo_matrix_rotate(&matrix, -M_PI_2);
cairo_matrix_translate(&matrix, -sw, 0);
break;
case FastivIoOrientation180:
cairo_matrix_scale(&matrix, -1, -1);
cairo_matrix_translate(&matrix, -sw, -sh);
break;
case FastivIoOrientation270:
cairo_matrix_rotate(&matrix, +M_PI_2);
cairo_matrix_translate(&matrix, 0, -sh);
break;
case FastivIoOrientationMirror0:
cairo_matrix_scale(&matrix, -1, +1);
cairo_matrix_translate(&matrix, -sw, 0);
break;
case FastivIoOrientationMirror90:
cairo_matrix_rotate(&matrix, +M_PI_2);
cairo_matrix_scale(&matrix, -1, +1);
cairo_matrix_translate(&matrix, -sw, -sh);
break;
case FastivIoOrientationMirror180:
cairo_matrix_scale(&matrix, +1, -1);
cairo_matrix_translate(&matrix, 0, -sh);
break;
case FastivIoOrientationMirror270:
cairo_matrix_rotate(&matrix, -M_PI_2);
cairo_matrix_scale(&matrix, -1, +1);
default:
break;
}
cairo_matrix_t matrix = get_orientation_matrix(self->orientation, sw, sh);
cairo_pattern_t *pattern = cairo_get_source(cr);
cairo_pattern_set_matrix(pattern, &matrix);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD);
@ -563,18 +569,78 @@ show_error_dialog(GtkWindow *parent, GError *error)
g_error_free(error);
}
static GtkWindow *
get_toplevel(GtkWidget *widget)
{
if (GTK_IS_WINDOW((widget = gtk_widget_get_toplevel(widget))))
return GTK_WINDOW(widget);
return NULL;
}
static void
on_draw_page(G_GNUC_UNUSED GtkPrintOperation *operation,
GtkPrintContext *context, G_GNUC_UNUSED int page_nr, FastivView *self)
{
double surface_width_px = 0, surface_height_px = 0;
get_surface_dimensions(self, &surface_width_px, &surface_height_px);
// Any DPI will be wrong, unless we import that information from the image.
double scale = 1 / 96.;
double w = surface_width_px * scale, h = surface_height_px * scale;
// Scale down to fit the print area, taking care to not divide by zero.
double areaw = gtk_print_context_get_width(context);
double areah = gtk_print_context_get_height(context);
scale *= fmin((areaw < w) ? areaw / w : 1, (areah < h) ? areah / h : 1);
cairo_t *cr = gtk_print_context_get_cairo_context(context);
cairo_scale(cr, scale, scale);
cairo_set_source_surface(cr, self->frame, 0, 0);
cairo_matrix_t matrix = get_orientation_matrix(
self->orientation, surface_width_px, surface_height_px);
cairo_pattern_set_matrix(cairo_get_source(cr), &matrix);
cairo_paint(cr);
}
static gboolean
print(FastivView *self)
{
GtkPrintOperation *print = gtk_print_operation_new();
gtk_print_operation_set_n_pages(print, 1);
gtk_print_operation_set_embed_page_setup(print, TRUE);
gtk_print_operation_set_unit(print, GTK_UNIT_INCH);
gtk_print_operation_set_job_name(print, "Image");
g_signal_connect(print, "draw-page", G_CALLBACK(on_draw_page), self);
static GtkPrintSettings *settings = NULL;
if (settings != NULL)
gtk_print_operation_set_print_settings(print, settings);
GError *error = NULL;
GtkWindow *window = get_toplevel(GTK_WIDGET(self));
GtkPrintOperationResult res = gtk_print_operation_run(
print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, window, &error);
if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
if (settings != NULL)
g_object_unref(settings);
settings = g_object_ref(gtk_print_operation_get_print_settings(print));
}
if (error)
show_error_dialog(window, error);
g_object_unref(print);
return TRUE;
}
static gboolean
save_as(FastivView *self, gboolean frame)
{
GtkWindow *window = NULL;
GtkWidget *widget = NULL;
if (GTK_IS_WINDOW((widget = gtk_widget_get_toplevel(GTK_WIDGET(self)))))
window = GTK_WINDOW(widget);
GtkWindow *window = get_toplevel(GTK_WIDGET(self));
GtkWidget *dialog = gtk_file_chooser_dialog_new(
frame ? "Save frame as" : "Save page as",
window, GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL, "_Save", GTK_RESPONSE_ACCEPT, NULL);
GtkWidget *dialog =
gtk_file_chooser_dialog_new(frame ? "Save frame as" : "Save page as",
window, GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL, "_Save", GTK_RESPONSE_ACCEPT, NULL);
GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
// TODO(p): Consider a hard dependency on libwebp, or clean this up.
@ -651,6 +717,8 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
return set_scale(self, self->scale * SCALE_STEP);
case GDK_KEY_minus:
return set_scale(self, self->scale / SCALE_STEP);
case GDK_KEY_p:
return print(self);
case GDK_KEY_s:
return save_as(self, FALSE);
case GDK_KEY_S:

View File

@ -21,7 +21,6 @@ libwebp = dependency('libwebp', required : get_option('libwebp'))
libwebpdemux = dependency('libwebpdemux', required : get_option('libwebp'))
libwebpdecoder = dependency('libwebpdecoder', required : get_option('libwebp'))
libwebpmux = dependency('libwebpmux', required : get_option('libwebp'))
libwebpencoder = dependency('libwebpencoder', required : get_option('libwebp'))
libheif = dependency('libheif', required : get_option('libheif'))
libtiff = dependency('libtiff-4', required : get_option('libtiff'))
gdkpixbuf = dependency('gdk-pixbuf-2.0', required : get_option('gdk-pixbuf'))
@ -38,7 +37,6 @@ dependencies = [
libwebpdemux,
libwebpdecoder,
libwebpmux,
libwebpencoder,
libheif,
libtiff,
gdkpixbuf,