Skip to content
This repository was archived by the owner on Mar 30, 2022. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions cairo_png.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include <assert.h>
#include <cairo.h>
#include <fcntl.h>
#include <limits.h>
#include <png.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "cairo_png.h"

cairo_status_t cairo_surface_write_to_png_mem(cairo_surface_t *sfc,
unsigned char **data,
unsigned long *len) {
int png_color_type;
int bpc;
#ifdef PNG_pHYs_SUPPORTED
double dpix, dpiy;
#endif

int height = cairo_image_surface_get_height(sfc);
int width = cairo_image_surface_get_width(sfc);
cairo_format_t cformat = cairo_image_surface_get_format(sfc);

/* PNG complains about "Image width or height is zero in IHDR" */
if (width == 0 || height == 0) {
return CAIRO_STATUS_WRITE_ERROR;
}

png_structp png =
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}

png_infop info = png_create_info_struct(png);
if (info == NULL) {
png_destroy_write_struct(&png, &info);
return CAIRO_STATUS_NO_MEMORY;
}

#ifdef PNG_SETJMP_SUPPORTED
if (setjmp(png_jmpbuf(png))) {
png_destroy_write_struct(&png, &info);
return 2;
}
#endif

//png_set_write_fn(png, &state, my_png_write_data, png_simple_output_flush_fn);

FILE *f = fopen("/tmp/meow.png", "wb");
if (f == NULL) {
fprintf(stderr, "failed to open output file\n");
exit(EXIT_FAILURE);
}
png_init_io(png, f);

switch (cformat) {
case CAIRO_FORMAT_ARGB32:
bpc = 8;
png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
case CAIRO_FORMAT_INVALID:
default:
png_destroy_write_struct(&png, &info);
return CAIRO_STATUS_INVALID_FORMAT;
}

png_set_IHDR(png, info, width, height, bpc, png_color_type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);

#ifdef PNG_pHYs_SUPPORTED
cairo_surface_get_fallback_resolution(sfc, &dpix, &dpiy);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should get the px/mm value from the monitor's physical size: https://github.com/emersion/grim/blob/master/main.c#L113

Copy link
Contributor Author

@ammgws ammgws Dec 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Righto. For the case of a single output it would just be something like pixels_x/physical_width, but now I'm wondering how this should work for when the screenshot contains multiple outputs.

something like get_output_layout_extents / (sum of all physical sizes in the layout)?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, except the screenshot region could partially span over multiple outputs. In general it may be even more complicated because different outputs could cover the same region.

I wonder if there's a good way to handle this.

png_set_pHYs(png, info, dpix * 1000 / 25.4, dpiy * 1000 / 25.4,
PNG_RESOLUTION_METER);
#endif

png_write_info(png, info);

for (size_t i = 0; i < (size_t)height; ++i) {
png_bytep row = (png_byte *)cairo_image_surface_get_data(sfc) + i * cairo_image_surface_get_stride(sfc);
png_write_row(png, row);
}

png_write_end(png, info);

return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t cj_write(void *closure, const unsigned char *data,
unsigned int length) {
if (write((long)closure, data, length) < (ssize_t)length) {
return CAIRO_STATUS_WRITE_ERROR;
} else {
return CAIRO_STATUS_SUCCESS;
}
}

cairo_status_t cairo_surface_write_to_png_stream(cairo_surface_t *sfc,
cairo_write_func_t write_func,
void *closure) {
cairo_status_t e;
unsigned char *data = NULL;
unsigned long len = 0;

e = cairo_surface_write_to_png_mem(sfc, &data, &len);
if (e == CAIRO_STATUS_SUCCESS) {
assert(sizeof(unsigned long) <= sizeof(size_t) ||
!(len >> (sizeof(size_t) * CHAR_BIT)));
e = write_func(closure, data, len);
free(data);
}

return e;
}

cairo_status_t cairo_surface_write_to_png(cairo_surface_t *sfc,
const char *filename) {
cairo_status_t e;
int outfile;

outfile =
open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

if (outfile == -1) {
return CAIRO_STATUS_DEVICE_ERROR;
}

e = cairo_surface_write_to_png_stream(sfc, cj_write, (void *)(long)outfile);

close(outfile);
return e;
}
10 changes: 10 additions & 0 deletions include/cairo_png.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef _CAIRO_PNG_H
#define _CAIRO_PNG_H

#include <cairo.h>

cairo_status_t cairo_surface_write_to_png_mem(cairo_surface_t *sfc, unsigned char **data, unsigned long *len);
cairo_status_t cairo_surface_write_to_png_stream(cairo_surface_t *sfc, cairo_write_func_t write_func, void *closure);
cairo_status_t cairo_surface_write_to_png(cairo_surface_t *sfc, const char *filename);

#endif
11 changes: 11 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output,

output->geometry.x = x;
output->geometry.y = y;
output->geometry.physical_width = physical_width;
output->geometry.physical_height = physical_height;
printf("debug phys: %dmm x %dmm\n", physical_width, physical_height);
output->transform = transform;
}

Expand Down Expand Up @@ -305,6 +308,7 @@ int main(int argc, char *argv[]) {

free(geometry);
geometry = calloc(1, sizeof(struct grim_box));

if (!parse_box(geometry, geometry_str)) {
fprintf(stderr, "invalid geometry\n");
return EXIT_FAILURE;
Expand Down Expand Up @@ -478,6 +482,12 @@ int main(int argc, char *argv[]) {
get_output_layout_extents(&state, geometry);
}

printf("debug output_layout_extents: %dpx x %dpx\n", geometry->width, geometry->height);
//TODO
//int px_per_mm_x = geometry->width/(output_layout_extents_physical);
//int px_per_mm_y = geometry->height/(output_layout_extents_physical);
//printf("px/mm values: %d x %d\n", pxmm_x, pxmm_y);

cairo_surface_t *surface = render(&state, geometry, scale);
if (surface == NULL) {
return EXIT_FAILURE;
Expand Down Expand Up @@ -519,6 +529,7 @@ int main(int argc, char *argv[]) {
#endif
}
}
printf("Status was: %d, success is %d", status, CAIRO_STATUS_SUCCESS);
if (status != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "%s\n", cairo_status_to_string(status));
if (status == CAIRO_STATUS_WRITE_ERROR && strlen(output_filepath) > NAME_MAX) {
Expand Down
3 changes: 3 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cc = meson.get_compiler('c')
cairo = dependency('cairo')
jpeg = dependency('libjpeg', required: get_option('jpeg'))
math = cc.find_library('m')
png = dependency('libpng')
realtime = cc.find_library('rt')
wayland_client = dependency('wayland-client')
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
Expand All @@ -37,12 +38,14 @@ grim_files = [
'output-layout.c',
'render.c',
'cairo_ppm.c',
'cairo_png.c',
]

grim_deps = [
cairo,
client_protos,
math,
png,
realtime,
wayland_client,
]
Expand Down