diff --git a/cairo_png.c b/cairo_png.c new file mode 100644 index 0000000..8dc6f63 --- /dev/null +++ b/cairo_png.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + 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; +} diff --git a/include/cairo_png.h b/include/cairo_png.h new file mode 100644 index 0000000..6f9a746 --- /dev/null +++ b/include/cairo_png.h @@ -0,0 +1,10 @@ +#ifndef _CAIRO_PNG_H +#define _CAIRO_PNG_H + +#include + +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 diff --git a/main.c b/main.c index a3c0268..9e548cf 100644 --- a/main.c +++ b/main.c @@ -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; } @@ -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; @@ -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; @@ -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) { diff --git a/meson.build b/meson.build index 5488e38..f669028 100644 --- a/meson.build +++ b/meson.build @@ -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') @@ -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, ]