Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

506 lines
13 KiB
C

/**
* libpsd - Photoshop file formats (*.psd) decode library
* Copyright (C) 2004-2007 Graphest Software.
*
* libpsd is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 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 Library General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: thumbnail.c, created by Patrick in 2006.05.31, libpsd@graphest.com Exp $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libpsd.h"
#include "psd_config.h"
#include "psd_system.h"
#include "psd_stream.h"
#include "psd_color.h"
#include "psd_math.h"
#ifdef PSD_INCLUDE_LIBJPEG
#include <setjmp.h>
#include "jpeglib.h"
/* we are a "source manager" as far as libjpeg is concerned */
#define JPEG_PROG_BUF_SIZE 32768
typedef struct {
struct jpeg_source_mgr pub; /* public fields */
JOCTET buffer[JPEG_PROG_BUF_SIZE]; /* start of buffer */
long skip_next; /* number of bytes to skip next read */
} my_source_mgr;
typedef my_source_mgr * my_src_ptr;
/* error handler data */
struct error_handler_data {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
/* progressive loader context */
typedef struct {
psd_uchar * pixels;
psd_uchar * dptr; /* current position in image_data */
psd_int width;
psd_int height;
psd_int number_channels;
psd_int row_stride;
boolean did_prescan;/* are we in image data yet? */
boolean got_header; /* have we loaded jpeg header? */
boolean src_initialized;/* TRUE when jpeg lib initialized */
boolean in_output; /* did we get suspended in an output pass? */
struct jpeg_decompress_struct cinfo;
struct error_handler_data jerr;
} JpegProgContext;
/**** Progressive image loading handling *****/
psd_static void fatal_error_handler (j_common_ptr cinfo)
{
/* FIXME:
* We should somehow signal what error occurred to the caller so the
* caller can handle the error message */
struct error_handler_data *errmgr;
errmgr = (struct error_handler_data *) cinfo->err;
cinfo->err->output_message (cinfo);
longjmp (errmgr->setjmp_buffer, 1);
}
/* these routines required because we are acting as a source manager for */
/* libjpeg. */
psd_static void init_source (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
src->skip_next = 0;
}
/* for progressive loading (called "I/O Suspension" by libjpeg docs) */
/* we do nothing except return "FALSE" */
psd_static boolean fill_input_buffer (j_decompress_ptr cinfo)
{
return FALSE;
}
psd_static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
long num_can_do;
/* move as far as we can into current buffer */
/* then set skip_next to catch the rest */
if (num_bytes > 0) {
num_can_do = PSD_MIN(src->pub.bytes_in_buffer, num_bytes);
src->pub.next_input_byte += num_can_do;
src->pub.bytes_in_buffer -= num_can_do;
src->skip_next = num_bytes - num_can_do;
}
}
psd_static void term_source (j_decompress_ptr cinfo)
{
/* XXXX - probably should scream something has happened */
}
/* explode gray image data from jpeg library into rgb components in pixbuf */
psd_static void explode_gray_into_buf (struct jpeg_decompress_struct *cinfo, psd_uchar **lines)
{
psd_int i, j;
psd_int w;
if(! (cinfo->output_components == 1))
return;
/* Expand grey->colour. Expand from the end of the
* memory down, so we can use the same buffer.
*/
w = cinfo->image_width;
for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
psd_uchar *from, *to;
from = lines[i] + w - 1;
to = lines[i] + (w - 1) * 3;
for (j = w - 1; j >= 0; j--) {
to[0] = from[0];
to[1] = from[0];
to[2] = from[0];
to -= 3;
from--;
}
}
}
psd_static void convert_cmyk_to_rgb (struct jpeg_decompress_struct *cinfo, psd_uchar **lines)
{
psd_int i, j;
if(! (cinfo->output_components == 4))
return;
for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
psd_uchar *p;
p = lines[i];
for (j = 0; j < cinfo->image_width; j++) {
psd_int c, m, y, k;
c = p[0];
m = p[1];
y = p[2];
k = p[3];
if (cinfo->saw_Adobe_marker) {
p[0] = k*c / 255;
p[1] = k*m / 255;
p[2] = k*y / 255;
}
else {
p[0] = (255 - k)*(255 - c) / 255;
p[1] = (255 - k)*(255 - m) / 255;
p[2] = (255 - k)*(255 - y) / 255;
}
p[3] = 255;
p += 4;
}
}
}
psd_static JpegProgContext * psd_jpeg_image_begin_load(void)
{
JpegProgContext * jpeg_context;
my_source_mgr *src;
jpeg_context = (JpegProgContext *)psd_malloc(sizeof(JpegProgContext));
if(jpeg_context == NULL)
return NULL;
memset(jpeg_context, 0, sizeof(JpegProgContext));
jpeg_context->got_header = FALSE;
jpeg_context->did_prescan = FALSE;
jpeg_context->src_initialized = FALSE;
jpeg_context->in_output = FALSE;
/* create libjpeg structures */
jpeg_create_decompress (&jpeg_context->cinfo);
jpeg_context->cinfo.src = (struct jpeg_source_mgr *)psd_malloc(sizeof(my_source_mgr));
src = (my_src_ptr) jpeg_context->cinfo.src;
jpeg_context->cinfo.err = jpeg_std_error (&jpeg_context->jerr.pub);
jpeg_context->jerr.pub.error_exit = fatal_error_handler;
src = (my_src_ptr) jpeg_context->cinfo.src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.term_source = term_source;
src->pub.bytes_in_buffer = 0;
src->pub.next_input_byte = NULL;
return jpeg_context;
}
psd_static boolean psd_jpeg_image_load_increment(JpegProgContext * jpeg_context, psd_uchar * buffer, psd_int size)
{
struct jpeg_decompress_struct *cinfo;
my_src_ptr src;
psd_int num_left, num_copy;
psd_int last_bytes_left;
psd_int spinguard;
boolean first;
const psd_uchar * bufhd;
src = (my_src_ptr) jpeg_context->cinfo.src;
cinfo = &jpeg_context->cinfo;
/* check for fatal error */
if (setjmp (jpeg_context->jerr.setjmp_buffer)) {
return FALSE;
}
/* skip over data if requested, handle psd_uint sizes cleanly */
/* only can happen if we've already called jpeg_get_header once */
if (jpeg_context->src_initialized && src->skip_next) {
if (src->skip_next > size) {
src->skip_next -= size;
return TRUE;
} else {
num_left = size - src->skip_next;
bufhd = buffer + src->skip_next;
src->skip_next = 0;
}
} else {
num_left = size;
bufhd = buffer;
}
if (num_left == 0)
return TRUE;
last_bytes_left = 0;
spinguard = 0;
first = TRUE;
while (TRUE) {
/* handle any data from caller we haven't processed yet */
if (num_left > 0) {
if(src->pub.bytes_in_buffer &&
src->pub.next_input_byte != src->buffer)
memmove(src->buffer, src->pub.next_input_byte,
src->pub.bytes_in_buffer);
num_copy = PSD_MIN(JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
num_left);
memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer += num_copy;
bufhd += num_copy;
num_left -= num_copy;
} else {
/* did anything change from last pass, if not return */
if (first) {
last_bytes_left = src->pub.bytes_in_buffer;
first = FALSE;
} else if (src->pub.bytes_in_buffer == last_bytes_left)
spinguard++;
else
last_bytes_left = src->pub.bytes_in_buffer;
}
/* should not go through twice and not pull bytes out of buffer */
if (spinguard > 2)
return TRUE;
/* try to load jpeg header */
if (!jpeg_context->got_header) {
psd_int rc;
rc = jpeg_read_header (cinfo, TRUE);
jpeg_context->src_initialized = TRUE;
if (rc == JPEG_SUSPENDED)
continue;
jpeg_context->got_header = TRUE;
} else if (!jpeg_context->did_prescan) {
psd_int rc;
/* start decompression */
cinfo->buffered_image = TRUE;
rc = jpeg_start_decompress (cinfo);
cinfo->do_fancy_upsampling = FALSE;
cinfo->do_block_smoothing = FALSE;
jpeg_context->number_channels = cinfo->output_components == 4 ? 4 : 3;
jpeg_context->pixels = (psd_uchar *)psd_malloc(cinfo->image_width *
cinfo->image_height * jpeg_context->number_channels);
if(jpeg_context->pixels == NULL)
return FALSE;
jpeg_context->width = cinfo->image_width;
jpeg_context->height = cinfo->image_height;
jpeg_context->row_stride = jpeg_context->width * jpeg_context->number_channels;
/* Use pixbuf buffer to store decompressed data */
jpeg_context->dptr = jpeg_context->pixels;
if (rc == JPEG_SUSPENDED)
continue;
jpeg_context->did_prescan = TRUE;
} else {
/* we're decompressing so feed jpeg lib scanlines */
psd_uchar *lines[4];
psd_uchar **lptr;
psd_uchar *rowptr;
psd_int nlines, i;
/* keep going until we've done all passes */
while (!jpeg_input_complete (cinfo)) {
if (!jpeg_context->in_output) {
if (jpeg_start_output (cinfo, cinfo->input_scan_number)) {
jpeg_context->in_output = TRUE;
jpeg_context->dptr = jpeg_context->pixels;
}
else
break;
}
/* keep going until we've done all scanlines */
while (cinfo->output_scanline < cinfo->output_height) {
lptr = lines;
rowptr = (psd_uchar *)jpeg_context->dptr;
for (i=0; i < cinfo->rec_outbuf_height; i++) {
*lptr++ = rowptr;
rowptr += jpeg_context->row_stride;
}
nlines = jpeg_read_scanlines (cinfo, lines,
cinfo->rec_outbuf_height);
if (nlines == 0)
break;
switch (cinfo->out_color_space) {
case JCS_GRAYSCALE:
explode_gray_into_buf (cinfo, lines);
break;
case JCS_RGB:
/* do nothing */
break;
case JCS_CMYK:
convert_cmyk_to_rgb (cinfo, lines);
break;
default:
return FALSE;
}
jpeg_context->dptr += nlines * jpeg_context->row_stride;
}
if (cinfo->output_scanline >= cinfo->output_height &&
jpeg_finish_output (cinfo))
jpeg_context->in_output = FALSE;
else
break;
}
if (jpeg_input_complete (cinfo))
/* did entire image */
return TRUE;
else
continue;
}
}
return TRUE;
}
psd_static void psd_jpeg_image_stop_load(JpegProgContext * jpeg_context)
{
psd_freeif(jpeg_context->pixels);
/* if we have an error? */
if (setjmp (jpeg_context->jerr.setjmp_buffer)) {
jpeg_destroy_decompress (&jpeg_context->cinfo);
} else
{
jpeg_finish_decompress(&jpeg_context->cinfo);
jpeg_destroy_decompress(&jpeg_context->cinfo);
}
if (jpeg_context->cinfo.src) {
my_src_ptr src = (my_src_ptr) jpeg_context->cinfo.src;
psd_free(src);
}
psd_free(jpeg_context);
}
psd_status psd_thumbnail_decode_jpeg(psd_argb_color ** dst_image, psd_int compress_len, psd_context * context)
{
psd_uchar * buffer;
JpegProgContext * jpeg_context;
psd_int length, i;
psd_argb_color * image_data, * data;
psd_uchar * pixels;
buffer = (psd_uchar *)psd_malloc(4096);
if(buffer == NULL)
return psd_status_malloc_failed;
jpeg_context = psd_jpeg_image_begin_load();
if(jpeg_context == NULL)
{
psd_free(buffer);
return psd_status_malloc_failed;
}
while(compress_len > 0)
{
length = psd_stream_get(context, buffer, PSD_MIN(4096, compress_len));
if(length > 0)
{
if(psd_jpeg_image_load_increment(jpeg_context, buffer, length) == psd_false)
{
psd_free(buffer);
psd_jpeg_image_stop_load(jpeg_context);
return psd_status_thumbnail_decode_error;
}
}
else if(length == 0)
{
psd_free(buffer);
psd_jpeg_image_stop_load(jpeg_context);
return psd_status_thumbnail_decode_error;
}
compress_len -= length;
}
image_data = (psd_argb_color *)psd_malloc(jpeg_context->width * jpeg_context->height * 4);
if(image_data == NULL)
{
psd_free(buffer);
psd_jpeg_image_stop_load(jpeg_context);
return psd_status_malloc_failed;
}
pixels = jpeg_context->pixels;
data = image_data;
if(jpeg_context->number_channels == 4)
{
for(i = jpeg_context->width * jpeg_context->height; i --; )
{
*data = PSD_ARGB_TO_COLOR(*(pixels + 3), *pixels, *(pixels + 1), *(pixels + 2));
data ++;
pixels += 4;
}
}
else
{
for(i = jpeg_context->width * jpeg_context->height; i --; )
{
*data = PSD_RGB_TO_COLOR(*pixels, *(pixels + 1), *(pixels + 2));
data ++;
pixels += 3;
}
}
*dst_image = image_data;
psd_free(buffer);
psd_jpeg_image_stop_load(jpeg_context);
return psd_status_done;
}
psd_status psd_thumbnail_decode_raw(psd_argb_color ** dst_image, psd_int image_len, psd_context * context)
{
// currently, we don't support the raw format
// since we don't meet any psd file with raw thumbnail
psd_assert(0);
psd_stream_get_null(context, image_len);
return psd_status_done;
}
#endif