Skip to content

Commit

Permalink
feat: added support for JPEG output
Browse files Browse the repository at this point in the history
  • Loading branch information
ntamas committed Sep 14, 2023
1 parent abcc829 commit cb0300f
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 58 deletions.
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.21)
project(stitch)

include(CheckIncludeFile)
include(CheckIPOSupported)
include(CheckSymbolExists)

# Set the path where we look for additional CMake modules
Expand Down Expand Up @@ -42,6 +43,9 @@ check_include_file("libintl.h" HAVE_LIBINTL_H)
check_include_file("unistd.h" HAVE_UNISTD_H)
check_symbol_exists(isatty unistd.h HAVE_ISATTY)

# Check for LTO support
check_ipo_supported(RESULT LTO_SUPPORTED)

# Turn on all compiler warnings
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")

Expand Down Expand Up @@ -85,3 +89,8 @@ if(GEOTIFF_FOUND AND PROJ_FOUND)
target_link_libraries(stitch ${GEOTIFF_LIBRARY} ${TIFF_LIBRARIES})
target_link_libraries(stitch PROJ::proj)
endif()

# Use link-time optimization if available
if(LTO_SUPPORTED)
set_property(TARGET stitch PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
8 changes: 5 additions & 3 deletions src/formats/geotiff.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
# include <geotiffio.h>
# include <xtiffio.h>

int write_geotiff(const char* outfile, int width, int height, unsigned char** rows, int px, int py, int minx, int maxy) {
int write_geotiff(const char* outfile, const image_t* img, int px, int py, int minx, int maxy) {
int i;
int width = img->width;
int height = img->height;

//TODO : Handle writing to stdout if required

Expand Down Expand Up @@ -57,7 +59,7 @@ int write_geotiff(const char* outfile, int width, int height, unsigned char** ro

//write raster image
for (i = 0; i < height; i++) {
if (!TIFFWriteScanline(tif, rows[i], i, 0)) {
if (!TIFFWriteScanline(tif, (void*) image_get_row_ptr(img, i), i, 0)) {
TIFFError("WriteImage", "failure in WriteScanline\n");
return EXIT_FAILURE;
}
Expand All @@ -76,7 +78,7 @@ int write_geotiff(const char* outfile, int width, int height, unsigned char** ro

#else /* GEOTIFF_FOUND */

int write_geotiff(const char* outfile, int width, int height, void* rows) {
int write_geotiff(const char* outfile, const image_t* img) {
fprintf(stderr, "stitch was compiled without GeoTIFF support, sorry\n");
return EXIT_FAILURE;
}
Expand Down
5 changes: 1 addition & 4 deletions src/formats/geotiff.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@

#include "image.h"

int write_geotiff(
const char* outfile, int width, int height, unsigned char** rows,
int px, int py, int minx, int maxy
);
int write_geotiff(const char* outfile, const image_t* img, int px, int py, int minx, int maxy);
48 changes: 48 additions & 0 deletions src/formats/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#include <stdlib.h>

int image_create(image_t* image, int width, int height, int depth) {
if (depth != 1 && depth != 3 && depth != 4) {
return EXIT_FAILURE;
}

image->width = width;
image->height = height;
image->depth = depth;
Expand All @@ -19,3 +23,47 @@ void image_destroy(image_t* image) {

image->width = image->height = image->depth = 0;
}

rgba_color_t image_get_pixel(const image_t* image, int x, int y) {
unsigned long long offset = (y * image->width + x) * image->depth;
rgba_color_t result;

if (image->depth == 4) {
result.r = image->buf[offset];
result.g = image->buf[offset + 1];
result.b = image->buf[offset + 2];
result.a = image->buf[offset + 3];
} else if (image->depth == 3) {
result.r = image->buf[offset];
result.g = image->buf[offset + 1];
result.b = image->buf[offset + 2];
result.a = 255;
} else if (image->depth == 1) {
result.r = image->buf[offset];
result.g = image->buf[offset];
result.b = image->buf[offset];
result.a = 255;
} else {
result.r = result.g = result.b = result.a = 0;
}

return result;
}

void image_set_pixel(image_t* image, int x, int y, rgba_color_t color) {
if (image->depth != 4) {
return;
}

unsigned long long offset = (y * image->width + x) * image->depth;

image->buf[offset] = color.r;
image->buf[offset + 1] = color.g;
image->buf[offset + 2] = color.b;
image->buf[offset + 3] = color.a;
}


const void* image_get_row_ptr(const image_t* image, int y) {
return (const void*)(image->buf + y * image->width * image->depth);
}
16 changes: 15 additions & 1 deletion src/formats/image.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
#pragma once

#include <stdint.h>

typedef struct image {
unsigned char *buf;
uint8_t *buf;
int depth;
int width;
int height;
} image_t;

Check warning on line 10 in src/formats/image.h

View workflow job for this annotation

GitHub Actions / windows-latest-hosted-ninja-vcpkg_submod-autocache

'image': '4' bytes padding added after data member 'height'

Check warning on line 10 in src/formats/image.h

View workflow job for this annotation

GitHub Actions / windows-latest-hosted-ninja-vcpkg_submod-autocache

'image': '4' bytes padding added after data member 'height'

Check warning on line 10 in src/formats/image.h

View workflow job for this annotation

GitHub Actions / windows-latest-hosted-ninja-vcpkg_submod-autocache

'image': '4' bytes padding added after data member 'height'

typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} rgba_color_t;

int image_create(image_t* image, int width, int height, int depth);
void image_destroy(image_t* image);

rgba_color_t image_get_pixel(const image_t* image, int x, int y);
void image_set_pixel(image_t* image, int x, int y, rgba_color_t color);

const void* image_get_row_ptr(const image_t* image, int y);
57 changes: 54 additions & 3 deletions src/formats/jpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@

#if JPEG_FOUND
# include <jpeglib.h>
# include <jerror.h>

int read_jpeg(char *s, int len, image_t *img) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);

jpeg_mem_src(&cinfo, (unsigned char *) s, len);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
Expand All @@ -39,8 +41,57 @@ int read_jpeg(char *s, int len, image_t *img) {
return EXIT_SUCCESS;
}

int write_jpeg(const char* outfile, int width, int height, void* rows) {
fprintf(stderr, "Not implemented yet, sorry\n");
int write_jpeg(const char* outfile, const image_t* img) {
FILE *outfp = stdout;
if (outfile != NULL) {
fprintf(stderr, "Output JPG: %s\n", outfile);
outfp = fopen(outfile, "wb");
if (outfp == NULL) {
perror(outfile);
exit(EXIT_FAILURE);
}
} else {
fprintf(stderr, "Output JPG: stdout\n");
}

struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
unsigned char* ptr;

outfp = fopen(outfile, "wb");
if (outfp == NULL) {
return EXIT_FAILURE;
}

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);

jpeg_stdio_dest(&cinfo, outfp);

cinfo.image_width = img->width;
cinfo.image_height = img->height;
cinfo.input_components = img->depth;
cinfo.in_color_space = JCS_EXT_RGBA;

jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 95, TRUE);

jpeg_start_compress(&cinfo, TRUE);

/* Write every scanline ... */
while(cinfo.next_scanline < cinfo.image_height) {
ptr = (void*) image_get_row_ptr(img, cinfo.next_scanline);
jpeg_write_scanlines(&cinfo, &ptr, 1);
}

jpeg_finish_compress(&cinfo);
if (outfile != NULL) {
fclose(outfp);
}

jpeg_destroy_compress(&cinfo);


return EXIT_FAILURE;
}

Expand All @@ -51,7 +102,7 @@ int read_jpeg(char *s, int len, image_t *img) {
return EXIT_FAILURE;
}

int write_jpeg(const char* outfile, int width, int height, void* rows) {
int write_jpeg(const char* outfile, const image_t* img) {
fprintf(stderr, "stitch was compiled without JPEG support, sorry\n");
return EXIT_FAILURE;
}
Expand Down
2 changes: 1 addition & 1 deletion src/formats/jpeg.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
#include "image.h"

int read_jpeg(char *s, int len, image_t* img);
int write_jpeg(const char* outfile, int width, int height, void* rows);
int write_jpeg(const char* outfile, const image_t* img);
19 changes: 16 additions & 3 deletions src/formats/png.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ int read_png(char *s, int len, image_t *img) {
return EXIT_SUCCESS;
}

int write_png(const char* outfile, int width, int height, void* rows) {
int write_png(const char* outfile, const image_t* img) {
FILE *outfp = stdout;
if (outfile != NULL) {
fprintf(stderr, "Output PNG: %s\n", outfile);
Expand All @@ -94,6 +94,7 @@ int write_png(const char* outfile, int width, int height, void* rows) {
}
png_structp png_ptr;
png_infop info_ptr;
void** rows;

png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, fail, fail, fail);
if (png_ptr == NULL) {
Expand All @@ -107,12 +108,24 @@ int write_png(const char* outfile, int width, int height, void* rows) {
return EXIT_FAILURE;
}

png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
rows = calloc(img->height, sizeof(void*));
if (rows == NULL) {
fprintf(stderr, "Not enough memory\n");
return EXIT_FAILURE;
}

for (int i = 0; i < img->height; i++) {
rows[i] = image_get_row_ptr(img, i);

Check warning on line 118 in src/formats/png.c

View workflow job for this annotation

GitHub Actions / ubuntu-latest-hosted-ninja-vcpkg_submod-autocache

assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]

Check warning on line 118 in src/formats/png.c

View workflow job for this annotation

GitHub Actions / macos-latest-hosted-ninja-vcpkg_submod-autocache

assigning to 'void *' from 'const void *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
}

png_set_IHDR(png_ptr, info_ptr, img->width, img->height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_rows(png_ptr, info_ptr, rows);

Check warning on line 122 in src/formats/png.c

View workflow job for this annotation

GitHub Actions / ubuntu-latest-hosted-ninja-vcpkg_submod-autocache

passing argument 3 of ‘png_set_rows’ from incompatible pointer type [-Wincompatible-pointer-types]

Check warning on line 122 in src/formats/png.c

View workflow job for this annotation

GitHub Actions / macos-latest-hosted-ninja-vcpkg_submod-autocache

incompatible pointer types passing 'void **' to parameter of type 'png_bytepp' (aka 'unsigned char **') [-Wincompatible-pointer-types]
png_init_io(png_ptr, outfp);
png_write_png(png_ptr, info_ptr, 0, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);

free(rows);

if (outfile != NULL) {
fclose(outfp);
}
Expand All @@ -128,7 +141,7 @@ int read_png(char *s, int len, image_t *img) {
}


int write_png(const char* outfile, int width, int height, void* rows) {
int write_png(const char* outfile, const image_t* img) {
fprintf(stderr, "stitch was compiled without PNG support, sorry\n");
return EXIT_FAILURE;
}
Expand Down
2 changes: 1 addition & 1 deletion src/formats/png.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
#include "image.h"

int read_png(char *s, int len, image_t* img);
int write_png(const char* outfile, int width, int height, void* rows);
int write_png(const char* outfile, const image_t* img);
Loading

0 comments on commit cb0300f

Please sign in to comment.