<commit_before>/*
    KindlePDFViewer: Picture viewer abstraction for Lua
    Copyright (C) 2012 <NAME> <<EMAIL>>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <setjmp.h>
#include <math.h>

#include "jpeglib.h"
#include "blitbuffer.h"
#include "drawcontext.h"
#include "pic.h"

#define MIN(a, b)      ((a) < (b) ? (a) : (b))
#define MAX(a, b)      ((a) > (b) ? (a) : (b))

typedef struct PicDocument {
	int width;
	int height;
	int components;
	uint8_t *image;
} PicDocument;

typedef struct PicPage {
	int width, height;
	PicDocument *doc;
} PicPage;

struct my_error_mgr {
	struct jpeg_error_mgr pub;
	jmp_buf setjmp_buffer;
};

typedef struct my_error_mgr *my_error_ptr;

METHODDEF(void) my_error_exit(j_common_ptr cinfo)
{
	my_error_ptr myerr = (my_error_ptr) cinfo->err;
	(*cinfo->err->output_message) (cinfo);
	longjmp(myerr->setjmp_buffer, 1);
}

uint8_t *readJPEG(const char *fname, int *width, int *height, int *components)
{
	struct jpeg_decompress_struct cinfo;
	struct my_error_mgr jerr;
	FILE *infile;
	JSAMPARRAY buffer;
	int row_stride;
	long cont;
	JSAMPLE *image_buffer;

	if ((infile = fopen(fname, "r")) == NULL) return NULL;
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	if (setjmp(jerr.setjmp_buffer)) {
		jpeg_destroy_decompress(&cinfo);
		fclose(infile);
		return NULL;
	}
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, infile);
	(void) jpeg_read_header(&cinfo, TRUE);
	(void) jpeg_start_decompress(&cinfo);
	row_stride = cinfo.output_width * cinfo.output_components;
	buffer = (*cinfo.mem->alloc_sarray)
		((j_common_ptr) & cinfo, JPOOL_IMAGE, row_stride, 1);

	image_buffer = (JSAMPLE *) malloc(cinfo.image_width*cinfo.image_height*cinfo.output_components);
	if (image_buffer == NULL) return NULL;
	*width = cinfo.image_width;
	*height = cinfo.image_height;

	//cont = cinfo.output_height - 1;
	cont = 0;
	while (cinfo.output_scanline < cinfo.output_height) {
		(void) jpeg_read_scanlines(&cinfo, buffer, 1);
		memcpy(image_buffer + cinfo.image_width * cinfo.output_components * cont, buffer[0], row_stride);
		cont++;
	}

	(void) jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	fclose(infile);
	*components = cinfo.output_components;
	return (uint8_t *)image_buffer;
}

/* Uses luminance match for approximating the human perception of colour,
 * as per http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
 * L = 0.299*Red + 0.587*Green + 0.114*Blue */
static uint8_t *rgbToGrayscale(uint8_t *image, int width, int height)
{
	int x, y;
	uint8_t *buf = malloc(width*height+1);

	if (!buf) return NULL;

	for (x = 0; x<width; x++)
		for (y=0; y<height; y++) {
			int pos = 3*(x+y*width);
			buf[x+y*width] = (uint8_t)(0.299*((double)image[pos]) + 0.587*((double)image[pos+1]) + 0.114*((double)image[pos+2]));
		}

	return buf;
}

static int openDocument(lua_State *L) {
	int width, height, components;
	const char *filename = luaL_checkstring(L, 1);

	PicDocument *doc = (PicDocument*) lua_newuserdata(L, sizeof(PicDocument));
	luaL_getmetatable(L, "picdocument");
	lua_setmetatable(L, -2);

	uint8_t *raw_image = readJPEG(filename, &width, &height, &components);
	if (!raw_image)
		return luaL_error(L, "Cannot open jpeg file");

	if (components == 1)
		doc->image = raw_image;
	else if (components == 3) {
		uint8_t *gray_image = rgbToGrayscale(raw_image, width, height);
		free(raw_image);
		if (!gray_image)
			return luaL_error(L, "Cannot convert to grayscale");
		else
			doc->image = gray_image;
	} else {
		free(raw_image);
		return luaL_error(L, "Unsupported image format");
	}

	doc->width = width;
	doc->height = height;
	doc->components = components;
	return 1;
}

static int openPage(lua_State *L) {
	PicDocument *doc = (PicDocument*) luaL_checkudata(L, 1, "picdocument");
	int pageno = luaL_checkint(L, 2);

	PicPage *page = (PicPage*) lua_newuserdata(L, sizeof(PicPage));
	luaL_getmetatable(L, "picpage");<commit_msg>Initialize doc->image in openDocument This is needed to protect against double free() in closeDocument().<commit_after>/*
    KindlePDFViewer: Picture viewer abstraction for Lua
    Copyright (C) 2012 <NAME> <<EMAIL>>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <setjmp.h>
#include <math.h>

#include "jpeglib.h"
#include "blitbuffer.h"
#include "drawcontext.h"
#include "pic.h"

#define MIN(a, b)      ((a) < (b) ? (a) : (b))
#define MAX(a, b)      ((a) > (b) ? (a) : (b))

typedef struct PicDocument {
	int width;
	int height;
	int components;
	uint8_t *image;
} PicDocument;

typedef struct PicPage {
	int width, height;
	PicDocument *doc;
} PicPage;

struct my_error_mgr {
	struct jpeg_error_mgr pub;
	jmp_buf setjmp_buffer;
};

typedef struct my_error_mgr *my_error_ptr;

METHODDEF(void) my_error_exit(j_common_ptr cinfo)
{
	my_error_ptr myerr = (my_error_ptr) cinfo->err;
	(*cinfo->err->output_message) (cinfo);
	longjmp(myerr->setjmp_buffer, 1);
}

uint8_t *readJPEG(const char *fname, int *width, int *height, int *components)
{
	struct jpeg_decompress_struct cinfo;
	struct my_error_mgr jerr;
	FILE *infile;
	JSAMPARRAY buffer;
	int row_stride;
	long cont;
	JSAMPLE *image_buffer;

	if ((infile = fopen(fname, "r")) == NULL) return NULL;
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	if (setjmp(jerr.setjmp_buffer)) {
		jpeg_destroy_decompress(&cinfo);
		fclose(infile);
		return NULL;
	}
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, infile);
	(void) jpeg_read_header(&cinfo, TRUE);
	(void) jpeg_start_decompress(&cinfo);
	row_stride = cinfo.output_width * cinfo.output_components;
	buffer = (*cinfo.mem->alloc_sarray)
		((j_common_ptr) & cinfo, JPOOL_IMAGE, row_stride, 1);

	image_buffer = (JSAMPLE *) malloc(cinfo.image_width*cinfo.image_height*cinfo.output_components);
	if (image_buffer == NULL) return NULL;
	*width = cinfo.image_width;
	*height = cinfo.image_height;

	//cont = cinfo.output_height - 1;
	cont = 0;
	while (cinfo.output_scanline < cinfo.output_height) {
		(void) jpeg_read_scanlines(&cinfo, buffer, 1);
		memcpy(image_buffer + cinfo.image_width * cinfo.output_components * cont, buffer[0], row_stride);
		cont++;
	}

	(void) jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	fclose(infile);
	*components = cinfo.output_components;
	return (uint8_t *)image_buffer;
}

/* Uses luminance match for approximating the human perception of colour,
 * as per http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
 * L = 0.299*Red + 0.587*Green + 0.114*Blue */
static uint8_t *rgbToGrayscale(uint8_t *image, int width, int height)
{
	int x, y;
	uint8_t *buf = malloc(width*height+1);

	if (!buf) return NULL;

	for (x = 0; x<width; x++)
		for (y=0; y<height; y++) {
			int pos = 3*(x+y*width);
			buf[x+y*width] = (uint8_t)(0.299*((double)image[pos]) + 0.587*((double)image[pos+1]) + 0.114*((double)image[pos+2]));
		}

	return buf;
}

static int openDocument(lua_State *L) {
	int width, height, components;
	const char *filename = luaL_checkstring(L, 1);

	PicDocument *doc = (PicDocument*) lua_newuserdata(L, sizeof(PicDocument));
	luaL_getmetatable(L, "picdocument");
	lua_setmetatable(L, -2);

	uint8_t *raw_image = readJPEG(filename, &width, &height, &components);
	if (!raw_image)
		return luaL_error(L, "Cannot open jpeg file");

	doc->image = NULL;
	if (components == 1)
		doc->image = raw_image;
	else if (components == 3) {
		uint8_t *gray_image = rgbToGrayscale(raw_image, width, height);
		free(raw_image);
		if (!gray_image)
			return luaL_error(L, "Cannot convert to grayscale");
		else
			doc->image = gray_image;
	} else {
		free(raw_image);
		return luaL_error(L, "Unsupported image format");
	}

	doc->width = width;
	doc->height = height;
	doc->components = components;
	return 1;
}

static int openPage(lua_State *L) {
	PicDocument *doc = (PicDocument*) luaL_checkudata(L, 1, "picdocument");
	int pageno = luaL_checkint(L, 2);

	PicPage *page = (PicPage*) lua_newuserdata(L, sizeof(PicPage));
	luaL_getmetatable(L, "picpage");
{'fraction_non_alphanumeric': 0.13179871520342612, 'fraction_numerical': 0.012205567451820129, 'mean_word_length': 4.841776110068793, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1572669', 'n_tokens_mistral': 3654, 'n_tokens_neox': 3418, 'n_words': 894}