BSOne.SFC/EM.Lib/ImageEn_SRC/Source/jpegfilt.pas

3775 lines
121 KiB
Plaintext

(* ImageEn Build 7.0.0.06.2637 @ 7-4-17 14:58:42.679 *)
(*
Copyright (c) 1998-2017 by Carlotta Calandra. All rights reserved.
Copyright (c) 2011-2017 by Xequte Software.
This software comes without express or implied warranty.
In no case shall the author be liable for any damage or unwanted behavior of any
computer hardware and/or software.
Author grants you the right to include the component
in your application, whether COMMERCIAL, SHAREWARE, or FREEWARE.
ImageEn, IEvolution and ImageEn ActiveX may not be included in any
commercial, shareware or freeware libraries or components.
www.ImageEn.com
*)
(*
File version 1005
*)
unit jpegfilt;
{$I ie.inc}
interface
{$Z4} // minimum enum size = 32 bit
uses
Windows, Graphics, classes, sysutils, ImageEnProc, ImageEnIO, hyiedefs, iexBitmaps;
function ReadJpegStream(Stream: TStream; TableStream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var xProgress: TProgressRec; Preview: boolean; Raw: boolean; ReadMetaTags: boolean; invertCMYK: boolean; freeICC: boolean; loadicc: boolean; MaxRows: integer; NativeFormat: boolean): integer;
procedure WriteJpegStream(Stream: TStream; bitmap: TIEBitmap; var IOParams: TIOParams; var xProgress: TProgressRec);
function JpegTryStream(Stream: TStream; find: boolean): int64;
function IEJpegInjectIPTC(InputStream, OutputStream: TStream; iptc: TIEIPTCInfoList; var xProgress: TProgressRec): boolean;
function IEJpegInjectEXIF(XInputStream, XOutputStream: TStream; exif: TIOParams; var xProgress: TProgressRec): boolean;
procedure IEJpegLosslessTransform(InStream, OutStream: TStream; var xProgress: TProgressRec; Transform: integer; GrayScale: boolean; CopyMarkers: integer; CutRect: TRect; updateEXIF: boolean);
function IEGetJpegQuality(InputStream: TStream; var QTables: pointer): integer;
function IEGetJpegLength(InputStream: TStream): integer;
procedure IEGetJpegICC(InputStream: TStream; Params: TIOParams);
procedure IEGetJPEGSize(const aFileName: string; var aWidth, aHeight: integer);
{ Known color spaces. }
type
IEJ_COLOR_SPACE = (
IEJCS_UNKNOWN, { error/unspecified }
IEJCS_GRAYSCALE, { monochrome }
IEJCS_RGB, { red/green/blue }
IEJCS_YCbCr, { Y/Cb/Cr (also known as YUV) }
IEJCS_CMYK, { C/M/Y/K }
IEJCS_YCCK { Y/Cb/Cr/K }
);
{ DCT/IDCT algorithm options. }
type
IEJ_DCT_METHOD = (
IEJDCT_ISLOW, { slow but accurate integer algorithm }
IEJDCT_IFAST, { faster, less accurate integer method }
IEJDCT_FLOAT { floating-point: accurate, fast on fast HW (Pentium)}
);
{ Dithering options for decompression. }
type
IEJ_DITHER_MODE = (
IEJDITHER_NONE, { no dithering }
IEJDITHER_ORDERED, { simple ordered dither }
IEJDITHER_FS { Floyd-Steinberg error diffusion dither }
);
type
IEJCOPY_OPTION = integer;
const
IEJXFORM_NONE = 0; // no transformation
IEJXFORM_CUT = 1; // cut out part of the image
IEJXFORM_FLIP_H = 2; // horizontal flip
IEJXFORM_FLIP_V = 3; // vertical flip
IEJXFORM_TRANSPOSE = 4; // transpose across UL-to-LR axis
IEJXFORM_TRANSVERSE = 5; // transpose across UR-to-LL axis
IEJXFORM_ROT_90 = 6; // 90-degree clockwise rotation
IEJXFORM_ROT_180 = 7; // 180-degree rotation
IEJXFORM_ROT_270 = 8; // 270-degree clockwise (or 90 ccw)
type
EIEJpegException = class(Exception);
{$ifndef IEUSEDLLJPEGLIB}
var
__turboFloat: LongBool = False;
{$endif}
const
M_SOF0 = $C0;
M_SOF1 = $C1;
M_SOF2 = $C2;
M_SOF3 = $C3;
M_SOF5 = $C5;
M_SOF6 = $C6;
M_SOF7 = $C7;
M_JPG = $C8;
M_SOF9 = $C9;
M_SOF10 = $CA;
M_SOF11 = $CB;
M_SOF13 = $CD;
M_SOF14 = $CE;
M_SOF15 = $CF;
M_DHT = $C4;
M_DAC = $CC;
M_RST0 = $D0;
M_RST1 = $D1;
M_RST2 = $D2;
M_RST3 = $D3;
M_RST4 = $D4;
M_RST5 = $D5;
M_RST6 = $D6;
M_RST7 = $D7;
M_SOI = $D8;
M_EOI = $D9;
M_SOS = $DA;
M_DQT = $DB;
M_DNL = $DC;
M_DRI = $DD;
M_DHP = $DE;
M_EXP = $DF;
M_APP0 = $E0;
M_APP1 = $E1;
M_APP2 = $E2;
M_APP3 = $E3;
M_APP4 = $E4;
M_APP5 = $E5;
M_APP6 = $E6;
M_APP7 = $E7;
M_APP8 = $E8;
M_APP9 = $E9;
M_APP10 = $EA;
M_APP11 = $EB;
M_APP12 = $EC;
M_APP13 = $ED;
M_APP14 = $EE;
M_APP15 = $EF;
M_JPG0 = $F0;
M_JPG1 = $F1;
M_JPG2 = $F2;
M_JPG3 = $F3;
M_JPG4 = $F4;
M_JPG5 = $F5;
M_JPG6 = $F6;
M_JPG7 = $F7;
M_JPG8 = $F8;
M_JPG9 = $F9;
M_JPG10 = $FA;
M_JPG11 = $FB;
M_JPG12 = $FC;
M_JPG13 = $FD;
M_COM = $FE;
M_TEM = $01;
M_BYPASS = $00;
implementation
uses
math, ieview, imageenview, ievision, iesettings, hyieutils;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// static jpeg library wrappers
{$ifndef IEUSEDLLJPEGLIB}
const
JPEG_LIB_VERSION = 62; { Version 6b }
JPEG_RST0 = $D0; { RST0 marker code }
JPEG_EOI = $D9; { EOI marker code }
DCTSIZE = 8; { The basic DCT block is 8x8 samples }
DCTSIZE2 = 64; { DCTSIZE squared; # of elements in a block }
NUM_QUANT_TBLS = 4; { Quantization tables are numbered 0..3 }
NUM_HUFF_TBLS = 4; { Huffman tables are numbered 0..3 }
NUM_ARITH_TBLS = 16; { Arith-coding tables are numbered 0..15 }
MAX_COMPS_IN_SCAN = 4; { JPEG limit on # of components in one scan }
MAX_SAMP_FACTOR = 4; { JPEG limit on sampling factors }
C_MAX_BLOCKS_IN_MCU = 10; { compressor's limit on blocks per MCU }
D_MAX_BLOCKS_IN_MCU = 10; { decompressor's limit on blocks per MCU }
MAX_COMPONENTS = 10; { maximum number of image components (color channels) }
MAXJSAMPLE = 255;
CENTERJSAMPLE = 128;
type
JSAMPLE = byte;
GETJSAMPLE = integer;
JCOEF = integer;
JCOEF_PTR = ^JCOEF;
UINT8 = byte;
UINT16 = Word;
UINT = Cardinal;
INT16 = SmallInt;
INT32 = Integer;
INT32PTR = ^INT32;
JDIMENSION = Cardinal;
JOCTET = Byte;
jTOctet = 0..(MaxInt div SizeOf(JOCTET)) - 1;
JOCTET_FIELD = array[jTOctet] of JOCTET;
JOCTET_FIELD_PTR = ^JOCTET_FIELD;
JOCTETPTR = ^JOCTET;
JSAMPLE_PTR = ^JSAMPLE;
JSAMPROW_PTR = ^JSAMPROW;
jTSample = 0..(MaxInt div SIZEOF(JSAMPLE)) - 1;
JSAMPLE_ARRAY = array[jTSample] of JSAMPLE; {far}
JSAMPROW = ^JSAMPLE_ARRAY; { ptr to one image row of pixel samples. }
jTRow = 0..(MaxInt div SIZEOF(JSAMPROW)) - 1;
JSAMPROW_ARRAY = array[jTRow] of JSAMPROW;
JSAMPARRAY = ^JSAMPROW_ARRAY; { ptr to some rows (a 2-D sample array) }
jTArray = 0..(MaxInt div SIZEOF(JSAMPARRAY)) - 1;
JSAMP_ARRAY = array[jTArray] of JSAMPARRAY;
JSAMPIMAGE = ^JSAMP_ARRAY; { a 3-D sample array: top index is color }
const
CSTATE_START = 100; { after create_compress }
CSTATE_SCANNING = 101; { start_compress done, write_scanlines OK }
CSTATE_RAW_OK = 102; { start_compress done, write_raw_data OK }
CSTATE_WRCOEFS = 103; { jpeg_write_coefficients done }
DSTATE_START = 200; { after create_decompress }
DSTATE_INHEADER = 201; { reading header markers, no SOS yet }
DSTATE_READY = 202; { found SOS, ready for start_decompress }
DSTATE_PRELOAD = 203; { reading multiscan file in start_decompress}
DSTATE_PRESCAN = 204; { performing dummy pass for 2-pass quant }
DSTATE_SCANNING = 205; { start_decompress done, read_scanlines OK }
DSTATE_RAW_OK = 206; { start_decompress done, read_raw_data OK }
DSTATE_BUFIMAGE = 207; { expecting jpeg_start_output }
DSTATE_BUFPOST = 208; { looking for SOS/EOI in jpeg_finish_output }
DSTATE_RDCOEFS = 209; { reading file in jpeg_read_coefficients }
DSTATE_STOPPING = 210; { looking for EOI in jpeg_finish_decompress }
{ Error handler }
const
JMSG_LENGTH_MAX = 200; { recommended size of format_message buffer }
JMSG_STR_PARM_MAX = 80;
JPOOL_PERMANENT = 0; // lasts until master record is destroyed
JPOOL_IMAGE = 1; // lasts until done with image/datastream
type
// The script for encoding a multiple-scan file is an array of these:
jpeg_scan_info_ptr = ^jpeg_scan_info;
jpeg_scan_info = record
comps_in_scan: Integer; // number of components encoded in this scan
component_index: array[0..MAX_COMPS_IN_SCAN - 1] of Integer; // their SOF/comp_info[] indexes
Ss, Se: Integer; // progressive JPEG spectral selection parms
Ah, Al: Integer; // progressive JPEG successive approx. parms
end;
// The decompressor can save APPn and COM markers in a list of these:
jpeg_saved_marker_ptr = ^jpeg_marker_struct;
jpeg_marker_struct = record
next: jpeg_saved_marker_ptr; // next in list, or NULL
marker: UINT8; // marker code: JPEG_COM, or JPEG_APP0+n
original_length: UINT; // # bytes of data in the file
data_length: UINT; // # bytes of data saved at data[]
data: ^JOCTET; // the data contained in the marker
// the marker length word is not counted in data_length or original_length
end;
jpeg_error_mgr_ptr = ^jpeg_error_mgr;
jpeg_progress_mgr_ptr = ^jpeg_progress_mgr;
j_common_ptr = ^jpeg_common_struct;
j_compress_ptr = ^jpeg_compress_struct;
j_decompress_ptr = ^jpeg_decompress_struct;
{ Routine signature for application-supplied marker processing methods.
Need not pass marker code since it is stored in cinfo^.unread_marker. }
jpeg_marker_parser_method = function(cinfo: j_decompress_ptr): LongBool;
{ Marker reading & parsing }
jpeg_marker_reader_ptr = ^jpeg_marker_reader;
jpeg_marker_reader = record
reset_marker_reader: procedure(cinfo: j_decompress_ptr);
{ Read markers until SOS or EOI.
Returns same codes as are defined for jpeg_consume_input:
JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. }
read_markers: function(cinfo: j_decompress_ptr): Integer;
{ Read a restart marker --- exported for use by entropy decoder only }
read_restart_marker: jpeg_marker_parser_method;
{ Application-overridable marker processing methods }
process_COM: jpeg_marker_parser_method;
process_APPn: array[0..16 - 1] of jpeg_marker_parser_method;
{ State of marker reader --- nominally internal, but applications
supplying COM or APPn handlers might like to know the state. }
saw_SOI: LongBool; { found SOI? }
saw_SOF: LongBool; { found SOF? }
next_restart_num: Integer; { next restart number expected (0-7) }
discarded_bytes: UINT; { # of bytes skipped looking for a marker }
end;
{int8array = Array[0..8-1] of int;}
int8array = array[0..8 - 1] of Integer;
jpeg_error_mgr = record
{ Error exit handler: does not return to caller }
error_exit: procedure(cinfo: j_common_ptr);
{ Conditionally emit a trace or warning message }
emit_message: procedure(cinfo: j_common_ptr; msg_level: Integer);
{ Routine that actually outputs a trace or error message }
output_message: procedure(cinfo: j_common_ptr);
{ Format a message string for the most recent JPEG error or message }
format_message: procedure(cinfo: j_common_ptr; buffer: PAnsiChar);
{ Reset error state variables at start of a new image }
reset_error_mgr: procedure(cinfo: j_common_ptr);
{ The message ID code and any parameters are saved here.
A message can have one string parameter or up to 8 int parameters. }
msg_code: Integer;
msg_parm: record
case byte of
0: (i: int8array);
1: (s: string[JMSG_STR_PARM_MAX]);
end;
trace_level: Integer; { max msg_level that will be displayed }
num_warnings: Integer; { number of corrupt-data warnings }
// ImageEn specific
aborting: pboolean;
end;
{ Data destination object for compression }
jpeg_destination_mgr_ptr = ^jpeg_destination_mgr;
jpeg_destination_mgr = record
next_output_byte: JOCTETptr; { => next byte to write in buffer }
free_in_buffer: Longint; { # of byte spaces remaining in buffer }
init_destination: procedure(cinfo: j_compress_ptr);
empty_output_buffer: function(cinfo: j_compress_ptr): LongBool;
term_destination: procedure(cinfo: j_compress_ptr);
end;
{ Data source object for decompression }
jpeg_source_mgr_ptr = ^jpeg_source_mgr;
jpeg_source_mgr = record
next_input_byte: JOCTETptr; { => next byte to read from buffer }
bytes_in_buffer: Longint; { # of bytes remaining in buffer }
init_source: procedure(cinfo: j_decompress_ptr);
fill_input_buffer: function(cinfo: j_decompress_ptr): LongBool;
skip_input_data: procedure(cinfo: j_decompress_ptr; num_bytes: Longint);
resync_to_restart: function(cinfo: j_decompress_ptr; desired: Integer): LongBool;
term_source: procedure(cinfo: j_decompress_ptr);
end;
{ JPEG library memory manager routines }
jpeg_memory_mgr_ptr = ^jpeg_memory_mgr;
jpeg_memory_mgr = record
{ Method pointers }
alloc_small: function(cinfo: j_common_ptr;
pool_id, sizeofobject: Integer): pointer;
alloc_large: function(cinfo: j_common_ptr;
pool_id, sizeofobject: Integer): pointer;
alloc_sarray: function(cinfo: j_common_ptr; pool_id: Integer;
samplesperrow: JDIMENSION;
numrows: JDIMENSION): JSAMPARRAY;
alloc_barray: pointer;
request_virt_sarray: pointer;
request_virt_barray: pointer;
realize_virt_arrays: pointer;
access_virt_sarray: pointer;
access_virt_barray: pointer;
free_pool: pointer;
self_destruct: pointer;
max_memory_to_use: Longint;
end;
{ Fields shared with jpeg_decompress_struct }// 24
jpeg_common_struct = record
err: jpeg_error_mgr_ptr; { Error handler module }
mem: jpeg_memory_mgr_ptr; { Memory manager module }
progress: jpeg_progress_mgr_ptr; { Progress monitor, or NIL if none }
client_data: Pointer; { Available for use by application }
is_decompressor: LongBool; { so common code can tell which is which }
global_state: Integer; { for checking call sequence validity }
end;
{ Progress monitor object }
jpeg_progress_mgr = record
progress_monitor: procedure(const cinfo: jpeg_common_struct);
pass_counter: Integer; { work units completed in this pass }
pass_limit: Integer; { total number of work units in this pass }
completed_passes: Integer; { passes completed so far }
total_passes: Integer; { total number of passes expected }
// extra info
end;
JQUANT_TBL_PTR = ^JQUANT_TBL;
JQUANT_TBL = packed record
{ This array gives the coefficient quantizers in natural array order
(not the zigzag order in which they are stored in a JPEG DQT marker).
CAUTION: IJG versions prior to v6a kept this array in zigzag order. }
quantval : Array[0..DCTSIZE2-1] of UINT16;
{ quantization step for each coefficient }
{ This field is used only during compression. It's initialized FALSE when
the table is created, and set TRUE when it's been output to the file.
You could suppress output of a table by setting this to TRUE.
(See jpeg_suppress_tables for an example.) }
sent_table : longbool; { TRUE when table has been output }
end;
JQUANT_TBL_FIELD = Array[0..(MaxInt div SizeOf(JQUANT_TBL))-1] of JQUANT_TBL;
jpeg_component_info_ptr = ^jpeg_component_info;
jpeg_component_info = packed record
{ These values are fixed over the whole image. }
{ For compression, they must be supplied by parameter setup; }
{ for decompression, they are read from the SOF marker. }
component_id : integer; { identifier for this component (0..255) }
component_index : integer; { its index in SOF or cinfo^.comp_info[] }
h_samp_factor : integer; { horizontal sampling factor (1..4) }
v_samp_factor : integer; { vertical sampling factor (1..4) }
quant_tbl_no : integer; { quantization table selector (0..3) }
{ These values may vary between scans. }
{ For compression, they must be supplied by parameter setup; }
{ for decompression, they are read from the SOS marker. }
{ The decompressor output side may not use these variables. }
dc_tbl_no : integer; { DC entropy table selector (0..3) }
ac_tbl_no : integer; { AC entropy table selector (0..3) }
{ Remaining fields should be treated as private by applications. }
{ These values are computed during compression or decompression startup:}
{ Component's size in DCT blocks.
Any dummy blocks added to complete an MCU are not counted; therefore
these values do not depend on whether a scan is interleaved or not. }
width_in_blocks : JDIMENSION;
height_in_blocks : JDIMENSION;
{ Size of a DCT block in samples. Always DCTSIZE for compression.
For decompression this is the size of the output from one DCT block,
reflecting any scaling we choose to apply during the IDCT step.
Values of 1, 2, 4, 8 are likely to be supported. Note that different
components may receive different IDCT scalings. }
DCT_scaled_size : integer;
{ The downsampled dimensions are the component's actual, unpadded number
of samples at the main buffer (preprocessing/compression interface), thus
downsampled_width = ceil(image_width * Hi/Hmax)
and similarly for height. For decompression, IDCT scaling is included, so
downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE)}
downsampled_width : JDIMENSION; { actual width in samples }
downsampled_height : JDIMENSION; { actual height in samples }
{ This flag is used only for decompression. In cases where some of the
components will be ignored (eg grayscale output from YCbCr image),
we can skip most computations for the unused components. }
component_needed : longbool; { do we need the value of this component? }
{ These values are computed before starting a scan of the component. }
{ The decompressor output side may not use these variables. }
MCU_width : integer; { number of blocks per MCU, horizontally }
MCU_height : integer; { number of blocks per MCU, vertically }
MCU_blocks : integer; { MCU_width * MCU_height }
MCU_sample_width : integer; { MCU width in samples, MCU_width*DCT_scaled_size }
last_col_width : integer; { # of non-dummy blocks across in last MCU }
last_row_height : integer; { # of non-dummy blocks down in last MCU }
{ Saved quantization table for component; NIL if none yet saved.
See jdinput.c comments about the need for this information.
This field is currently used only for decompression. }
quant_table : JQUANT_TBL_PTR;
{ Private per-component storage for DCT or IDCT subsystem. }
dct_table : pointer;
end; { record jpeg_component_info }
jTCinfo = 0..(MaxInt div SizeOf(jpeg_component_info))-1;
jpeg_component_info_array = array[jTCinfo] of jpeg_component_info;
jpeg_component_info_list_ptr = ^jpeg_component_info_array;
{ Master record for a compression instance }
// attenzione agli allineamenti tra delphi3-4 e Delphi5
jpeg_compress_struct = packed record
common: jpeg_common_struct; // 24
dest: jpeg_destination_mgr_ptr; { Destination for compressed data } // 4
{ Description of source image --- these fields must be filled in by
outer application before starting compression. in_color_space must
be correct before you can even call jpeg_set_defaults(). }
image_width: JDIMENSION; { input image width } // 4
image_height: JDIMENSION; { input image height } // 4
input_components: Integer; { # of color components in input image } // 4
in_color_space: IEJ_COLOR_SPACE; { colorspace of input image } // 4
input_gamma: double; { image gamma of input image } // 8
dummy1: integer;
// Compression parameters
data_precision: Integer; { bits of precision in image data } // 4
num_components: Integer; { # of color components in JPEG image } // 4
jpeg_color_space: IEJ_COLOR_SPACE; { colorspace of JPEG image } // 4
comp_info: jpeg_component_info_ptr; // 4
quant_tbl_ptrs: array[0..NUM_QUANT_TBLS - 1] of Pointer; // 16
dc_huff_tbl_ptrs: array[0..NUM_HUFF_TBLS - 1] of Pointer; // 16
ac_huff_tbl_ptrs: array[0..NUM_HUFF_TBLS - 1] of Pointer; // 16
arith_dc_L: array[0..NUM_ARITH_TBLS - 1] of UINT8; // 16 { L values for DC arith-coding tables }
arith_dc_U: array[0..NUM_ARITH_TBLS - 1] of UINT8; // 16 { U values for DC arith-coding tables }
arith_ac_K: array[0..NUM_ARITH_TBLS - 1] of UINT8; // 16 { Kx values for AC arith-coding tables }
num_scans: Integer; { # of entries in scan_info array } // 4
scan_info: Pointer; { script for multi-scan file, or NIL } // 4
raw_data_in: LongBool; { TRUE=caller supplies downsampled data } // 4
arith_code: LongBool; { TRUE=arithmetic coding, FALSE=Huffman } // 4
optimize_coding: LongBool; { TRUE=optimize entropy encoding parms } // 4
CCIR601_sampling: LongBool; { TRUE=first samples are cosited } // 4
smoothing_factor: Integer; { 1..100, or 0 for no input smoothing } // 4
dct_method: IEJ_DCT_METHOD; { DCT algorithm selector } // 4
restart_interval: UINT; { MCUs per restart, or 0 for no restart } // 4
restart_in_rows: Integer; { if > 0, MCU rows per restart interval } // 4
{ Parameters controlling emission of special markers. }
write_JFIF_header: LongBool; { should a JFIF marker be written? } // 4
JFIF_major_version: UINT8; // What to write for the JFIF version number // (4)
JFIF_minor_version: UINT8; // (4)
{ These three values are not used by the JPEG code, merely copied }
{ into the JFIF APP0 marker. density_unit can be 0 for unknown, }
{ 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect }
{ ratio is defined by X_density/Y_density even when density_unit=0. }
density_unit: UINT8; { JFIF code for pixel size units } // (4)
dummy2: UINT8;
X_density: UINT16; { Horizontal pixel density } // (4)
Y_density: UINT16; { Vertical pixel density } // (4)
write_Adobe_marker: LongBool; { should an Adobe marker be written? } // 4
{ State variable: index of next scanline to be written to
jpeg_write_scanlines(). Application may use this to control its
processing loop, e.g., "while (next_scanline < image_height)". }
next_scanline: JDIMENSION; { 0 .. image_height-1 } // 4
{ Remaining fields are known throughout compressor, but generally
should not be touched by a surrounding application. }
progressive_mode: LongBool; { TRUE if scan script uses progressive mode } // 4
max_h_samp_factor: Integer; { largest h_samp_factor } // 4
max_v_samp_factor: Integer; { largest v_samp_factor } // 4
total_iMCU_rows: JDIMENSION; { # of iMCU rows to be input to coef ctlr } // 4
comps_in_scan: Integer; { # of JPEG components in this scan } // 4
cur_comp_info: array[0..MAX_COMPS_IN_SCAN - 1] of Pointer; // 16
MCUs_per_row: JDIMENSION; { # of MCUs across the image } // 4
MCU_rows_in_scan: JDIMENSION; { # of MCU rows in the image } // 4
blocks_in_MCU: Integer; { # of DCT blocks per MCU } // 4
MCU_membership: array[0..C_MAX_BLOCKS_IN_MCU - 1] of Integer; // 40 (4*10)
Ss, Se, Ah, Al: Integer; { progressive JPEG parameters for scan } // 16
{ Links to compression subobjects (methods and private variables of modules) }
master: Pointer; // 4
main: Pointer; // 4
prep: Pointer; // 4
coef: Pointer; // 4
marker: Pointer; // 4
cconvert: Pointer; // 4
downsample: Pointer; // 4
fdct: Pointer; // 4
entropy: Pointer; // 4
script_space: jpeg_scan_info_ptr; // workspace for jpeg_simple_progression // 4
script_space_size: Integer; // 4
end;
{ Master record for a decompression instance }
jpeg_decompress_struct = packed record
common: jpeg_common_struct;
{ Source of compressed data }
src: jpeg_source_mgr_ptr;
{ Basic description of image --- filled in by jpeg_read_header(). }
{ Application may inspect these values to decide how to process image. }
image_width: JDIMENSION; { nominal image width (from SOF marker) }
image_height: JDIMENSION; { nominal image height }
num_components: Integer; { # of color components in JPEG image }
jpeg_color_space: IEJ_COLOR_SPACE; { colorspace of JPEG image }
{ Decompression processing parameters }
out_color_space: IEJ_COLOR_SPACE; { colorspace for output }
scale_num, scale_denom: uint; { fraction by which to scale image }
output_gamma: double; { image gamma wanted in output }
buffered_image: LongBool; { TRUE=multiple output passes }
raw_data_out: LongBool; { TRUE=downsampled data wanted }
dct_method: IEJ_DCT_METHOD; { IDCT algorithm selector }
do_fancy_upsampling: LongBool; { TRUE=apply fancy upsampling }
do_block_smoothing: LongBool; { TRUE=apply interblock smoothing }
quantize_colors: LongBool; { TRUE=colormapped output wanted }
{ the following are ignored if not quantize_colors: }
dither_mode: IEJ_DITHER_MODE; { type of color dithering to use }
two_pass_quantize: LongBool; { TRUE=use two-pass color quantization }
desired_number_of_colors: Integer; { max # colors to use in created colormap }
{ these are significant only in buffered-image mode: }
enable_1pass_quant: LongBool; { enable future use of 1-pass quantizer }
enable_external_quant: LongBool; { enable future use of external colormap }
enable_2pass_quant: LongBool; { enable future use of 2-pass quantizer }
{ Description of actual output image that will be returned to application.
These fields are computed by jpeg_start_decompress().
You can also use jpeg_calc_output_dimensions() to determine these values
in advance of calling jpeg_start_decompress(). }
output_width: JDIMENSION; { scaled image width }
output_height: JDIMENSION; { scaled image height }
out_color_components: Integer; { # of color components in out_color_space }
output_components: Integer; { # of color components returned }
{ output_components is 1 (a colormap index) when quantizing colors;
otherwise it equals out_color_components. }
rec_outbuf_height: Integer; { min recommended height of scanline buffer }
{ If the buffer passed to jpeg_read_scanlines() is less than this many
rows high, space and time will be wasted due to unnecessary data
copying. Usually rec_outbuf_height will be 1 or 2, at most 4. }
{ When quantizing colors, the output colormap is described by these
fields. The application can supply a colormap by setting colormap
non-NIL before calling jpeg_start_decompress; otherwise a colormap
is created during jpeg_start_decompress or jpeg_start_output. The map
has out_color_components rows and actual_number_of_colors columns. }
actual_number_of_colors: Integer; { number of entries in use }
colormap: JSAMPARRAY; { The color map as a 2-D pixel array }
{ State variables: these variables indicate the progress of decompression.
The application may examine these but must not modify them. }
{ Row index of next scanline to be read from jpeg_read_scanlines().
Application may use this to control its processing loop, e.g.,
"while (output_scanline < output_height)". }
output_scanline: JDIMENSION; { 0 .. output_height-1 }
{ Current input scan number and number of iMCU rows completed in scan.
These indicate the progress of the decompressor input side. }
input_scan_number: Integer; { Number of SOS markers seen so far }
input_iMCU_row: JDIMENSION; { Number of iMCU rows completed }
{ The "output scan number" is the notional scan being displayed by the
output side. The decompressor will not allow output scan/row number
to get ahead of input scan/row, but it can fall arbitrarily far behind.}
output_scan_number: Integer; { Nominal scan number being displayed }
output_iMCU_row: Integer; { Number of iMCU rows read }
coef_bits: Pointer;
{ Internal JPEG parameters --- the application usually need not look at
these fields. Note that the decompressor output side may not use
any parameters that can change between scans. }
{ Quantization and Huffman tables are carried forward across input
datastreams when processing abbreviated JPEG datastreams. }
quant_tbl_ptrs: array[0..NUM_QUANT_TBLS - 1] of Pointer;
dc_huff_tbl_ptrs: array[0..NUM_HUFF_TBLS - 1] of Pointer;
ac_huff_tbl_ptrs: array[0..NUM_HUFF_TBLS - 1] of Pointer;
{ These parameters are never carried across datastreams, since they
are given in SOF/SOS markers or defined to be reset by SOI. }
data_precision: Integer; { bits of precision in image data }
comp_info: Pointer;
progressive_mode: LongBool; { TRUE if SOFn specifies progressive mode }
arith_code: LongBool; { TRUE=arithmetic coding, FALSE=Huffman }
arith_dc_L: array[0..NUM_ARITH_TBLS - 1] of UINT8; { L values for DC arith-coding tables }
arith_dc_U: array[0..NUM_ARITH_TBLS - 1] of UINT8; { U values for DC arith-coding tables }
arith_ac_K: array[0..NUM_ARITH_TBLS - 1] of UINT8; { Kx values for AC arith-coding tables }
restart_interval: UINT; { MCUs per restart interval, or 0 for no restart }
{ These fields record data obtained from optional markers recognized by
the JPEG library. }
saw_JFIF_marker: LongBool; { TRUE iff a JFIF APP0 marker was found }
JFIF_major_version: UINT8; // What to write for the JFIF version number
JFIF_minor_version: UINT8;
{ Data copied from JFIF marker: }
density_unit: UINT8; { JFIF code for pixel size units }
dummy2: UINT8;
X_density: UINT16; { Horizontal pixel density }
Y_density: UINT16; { Vertical pixel density }
saw_Adobe_marker: LongBool; { TRUE iff an Adobe APP14 marker was found }
Adobe_transform: UINT8; { Color transform code from Adobe marker }
dummy3: UINT8;
dummy4: UINT8;
dummy5: UINT8;
CCIR601_sampling: LongBool; { TRUE=first samples are cosited }
(* Aside from the specific data retained from APPn markers known to the
* library, the uninterpreted contents of any or all APPn and COM markers
* can be saved in a list for examination by the application.
*)
marker_list: jpeg_saved_marker_ptr; // Head of list of saved markers
{ Remaining fields are known throughout decompressor, but generally
should not be touched by a surrounding application. }
max_h_samp_factor: Integer; { largest h_samp_factor }
max_v_samp_factor: Integer; { largest v_samp_factor }
min_DCT_scaled_size: Integer; { smallest DCT_scaled_size of any component }
total_iMCU_rows: JDIMENSION; { # of iMCU rows in image }
sample_range_limit: Pointer; { table for fast range-limiting }
{ These fields are valid during any one scan.
They describe the components and MCUs actually appearing in the scan.
Note that the decompressor output side must not use these fields. }
comps_in_scan: Integer; { # of JPEG components in this scan }
cur_comp_info: array[0..MAX_COMPS_IN_SCAN - 1] of Pointer;
MCUs_per_row: JDIMENSION; { # of MCUs across the image }
MCU_rows_in_scan: JDIMENSION; { # of MCU rows in the image }
blocks_in_MCU: JDIMENSION; { # of DCT blocks per MCU }
MCU_membership: array[0..D_MAX_BLOCKS_IN_MCU - 1] of Integer;
Ss, Se, Ah, Al: Integer; { progressive JPEG parameters for scan }
{ This field is shared between entropy decoder and marker parser.
It is either zero or the code of a JPEG marker that has been
read from the data source, but has not yet been processed. }
unread_marker: Integer;
{ Links to decompression subobjects
(methods, private variables of modules) }
master: Pointer;
main: Pointer;
coef: Pointer;
post: Pointer;
inputctl: Pointer;
marker: Pointer;
entropy: Pointer;
idct: Pointer;
upsample: Pointer;
cconvert: Pointer;
cquantize: Pointer;
end;
TIEJPEGDESTMGR = record
pub: JPEG_DESTINATION_MGR;
fs: TStream;
aborting: pboolean;
buffer: JOCTETPTR;
end;
PIEJPEGDESTMGR = ^TIEJPEGDESTMGR;
TIEJPEGSOURCEMGR = record
pub: JPEG_SOURCE_MGR;
fs: TStream;
start_of_file: boolean;
aborting: pboolean;
buffer: JOCTETPTR;
end;
PIEJPEGSOURCEMGR = ^TIEJPEGSOURCEMGR;
{ Decompression startup: read start of JPEG datastream to see what's there
function jpeg_read_header (cinfo : j_decompress_ptr;
require_image : LongBool) : Integer;
Return value is one of: }
const
JPEG_SUSPENDED = 0; { Suspended due to lack of input data }
JPEG_HEADER_OK = 1; { Found valid image datastream }
JPEG_HEADER_TABLES_ONLY = 2; { Found valid table-specs-only datastream }
{ If you pass require_image = TRUE (normal case), you need not check for
a TABLES_ONLY return code; an abbreviated file will cause an error exit.
JPEG_SUSPENDED is only possible if you use a data source module that can
give a suspension return (the stdio source module doesn't). }
{ function jpeg_consume_input (cinfo : j_decompress_ptr) : Integer;
Return value is one of: }
JPEG_REACHED_SOS = 1; { Reached start of new scan }
JPEG_REACHED_EOI = 2; { Reached end of image }
JPEG_ROW_COMPLETED = 3; { Completed one iMCU row }
JPEG_SCAN_COMPLETED = 4; { Completed last iMCU row of a scan }
// Stubs for external C RTL functions referenced by JPEG OBJ files.
function _malloc(size: Integer): Pointer; cdecl;
begin
result := IEAutoAlloc(size);
end;
procedure _free(P: Pointer); cdecl;
begin
IEAutoFree(P);
end;
procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl;
begin
FillChar(P^, count, B);
end;
procedure _memcpy(dest, source: Pointer; count: Integer); cdecl;
begin
Move(source^, dest^, count);
end;
function __ftol: Integer;
var
f: double;
begin
asm
lea eax, f // BC++ passes floats on the FPU stack
fstp qword ptr [eax] // Delphi passes floats on the CPU stack
end;
Result := Trunc(f);
end;
{$L ie_jdapimin.obj}
{$L ie_jmemmgr.obj}
{$L ie_jmemnobs.obj}
{$L ie_jdinput.obj}
{$L ie_jdapistd.obj}
{$L ie_jdmaster.obj}
{$L ie_jdphuff.obj}
{$L ie_jdhuff.obj}
{$L ie_jdmerge.obj}
{$L ie_jdcolor.obj}
{$L ie_jquant1.obj}
{$L ie_jquant2.obj}
{$L ie_jdmainct.obj}
{$L ie_jdcoefct.obj}
{$L ie_jdpostct.obj}
{$L ie_jddctmgr.obj}
{$L ie_jdsample.obj}
{$L ie_jidctflt.obj}
{$L ie_jidctfst.obj}
{$L ie_jidctint.obj}
{$L ie_jidctred.obj}
{$L ie_jdmarker.obj}
{$L ie_jutils.obj}
{$L ie_jcomapi.obj}
{$L ie_transupp.obj}
{$L ie_jdtrans.obj}
{$L ie_jctrans.obj}
procedure jpeg_CreateDecompress(var cinfo: jpeg_decompress_struct; version: integer; structsize: integer); external;
procedure jpeg_read_header(var cinfo: jpeg_decompress_struct; RequireImage: LongBool); external;
procedure jpeg_calc_output_dimensions(var cinfo: jpeg_decompress_struct); external;
function jpeg_start_decompress(var cinfo: jpeg_decompress_struct): Longbool; external;
function jpeg_read_scanlines(var cinfo: jpeg_decompress_struct; scanlines: JSAMPARRAY; max_lines: JDIMENSION): JDIMENSION; external;
function jpeg_finish_decompress(var cinfo: jpeg_decompress_struct): Longbool; external;
procedure jpeg_destroy_decompress(var cinfo: jpeg_decompress_struct); external;
function jpeg_has_multiple_scans(var cinfo: jpeg_decompress_struct): Longbool; external;
function jpeg_consume_input(var cinfo: jpeg_decompress_struct): Integer; external;
function jpeg_start_output(var cinfo: jpeg_decompress_struct; scan_number: Integer): Longbool; external;
function jpeg_finish_output(var cinfo: jpeg_decompress_struct): LongBool; external;
procedure jpeg_destroy(var cinfo: jpeg_common_struct); external;
procedure jpeg_save_markers(var cinfo: jpeg_decompress_struct; marker_code: integer; length_limit: UINT); external;
function jpeg_resync_to_restart(cinfo: j_decompress_ptr; desired: Integer): LongBool; external;
// transforms
type
JXFORM_CODE = integer;
type
j_transform_info_ptr = ^jpeg_transform_info;
jpeg_transform_info = record
// Options: set by caller */
transform: JXFORM_CODE; // image transform operator
trim: integer; // if TRUE, trim partial MCUs as needed
force_grayscale: integer; // if TRUE, convert color image to grayscale
xoffs, yoffs, newwidth, newheight: dword;
// Internal workspace: caller should not touch these
num_components: integer; // # of components in workspace
workspace_coef_arrays: pointer; // workspace for transformations
end;
const
JCOPYOPT_NONE = 0; // copy no optional markers
JCOPYOPT_COMMENTS = 1; // copy only comment (COM) markers
JCOPYOPT_ALL = 2; // copy all optional markers
procedure jtransform_request_workspace(var srcinfo: jpeg_decompress_struct; var info: jpeg_transform_info); external;
function jtransform_adjust_parameters(var srcinfo: jpeg_decompress_struct; var dstinfo: jpeg_compress_struct;
src_coef_arrays: pointer;
var info: jpeg_transform_info): pointer; external;
procedure jtransform_execute_transformation(var srcinfo: jpeg_decompress_struct; var dstinfo: jpeg_compress_struct;
src_coef_arrays: pointer;
var info: jpeg_transform_info); external;
procedure jcopy_markers_setup(var srcinfo: jpeg_decompress_struct; option: IEJCOPY_OPTION); external;
procedure jcopy_markers_execute(var srcinfo: jpeg_decompress_struct; var dstinfo: jpeg_compress_struct;
option: IEJCOPY_OPTION); external;
function jpeg_read_coefficients(var cinfo: jpeg_decompress_struct): pointer; external;
procedure jpeg_write_coefficients(var dstinfo: jpeg_compress_struct; coef: pointer); external;
procedure jpeg_copy_critical_parameters(var srcinfo: jpeg_decompress_struct; var dstinfo: jpeg_compress_struct); external;
{$L ie_jcparam.obj}
{$L ie_jcapistd.obj}
{$L ie_jcapimin.obj}
{$L ie_jcinit.obj}
{$L ie_jcmarker.obj}
{$L ie_jcmaster.obj}
{$L ie_jcmainct.obj}
{$L ie_jcprepct.obj}
{$L ie_jccoefct.obj}
{$L ie_jccolor.obj}
{$L ie_jcsample.obj}
{$L ie_jcdctmgr.obj}
{$L ie_jcphuff.obj}
{$L ie_jfdctint.obj}
{$L ie_jfdctfst.obj}
{$L ie_jfdctflt.obj}
{$L ie_jchuff.obj}
procedure jpeg_CreateCompress(var cinfo: jpeg_compress_struct; version: integer; structsize: integer); external;
procedure jpeg_set_defaults(var cinfo: jpeg_compress_struct); external;
procedure jpeg_set_quality(var cinfo: jpeg_compress_struct; Quality: Integer; Baseline: Longbool); external;
procedure jpeg_set_colorspace(var cinfo: jpeg_compress_struct; colorspace: IEJ_COLOR_SPACE); external;
procedure jpeg_simple_progression(var cinfo: jpeg_compress_struct); external;
procedure jpeg_start_compress(var cinfo: jpeg_compress_struct; WriteAllTables: LongBool); external;
function jpeg_write_scanlines(var cinfo: jpeg_compress_struct; scanlines: JSAMPARRAY; max_lines: JDIMENSION): JDIMENSION; external;
procedure jpeg_finish_compress(var cinfo: jpeg_compress_struct); external;
procedure jpeg_destroy_compress(var cinfo: jpeg_compress_struct); external;
procedure jpeg_write_marker(var cinfo: jpeg_compress_struct; marker: integer; dataptr: pbyte; datalen: dword); external;
/////////////////////////////////////////////////////////////////////////////////////
// error manager
procedure JpegError(cinfo: j_common_ptr);
var
ss: string;
c: integer;
begin
if assigned(cinfo.err^.aborting) then
cinfo.err^.aborting^ := True;
c := cinfo.err^.msg_code + 1;
case c of
1: ss := 'Bogus message code ?';
2: ss := 'Sorry, there are legal restrictions on arithmetic coding';
3: ss := 'ALIGN_TYPE is wrong, please fix';
4: ss := 'MAX_ALLOC_CHUNK is wrong, please fix';
5: ss := 'Bogus buffer control mode';
6: ss := 'Invalid component ID ? in SOS';
7: ss := 'DCT coefficient out of range';
8: ss := 'IDCT output block size ? not supported';
9: ss := 'Bogus Huffman table definition';
10: ss := 'Bogus input colorspace';
11: ss := 'Bogus JPEG colorspace';
12: ss := 'Bogus marker length';
13: ss := 'Wrong JPEG library version: library is ?, caller expects ?';
14: ss := 'Sampling factors too large for interleaved scan';
15: ss := 'Invalid memory pool code ?';
16: ss := 'Unsupported JPEG data precision ?';
17: ss := 'Invalid progressive parameters Ss=? Se=? Ah=? Al=?';
18: ss := 'Invalid progressive parameters at scan script entry ?';
19: ss := 'Bogus sampling factors';
20: ss := 'Invalid scan script at entry ?';
21: ss := 'Improper call to JPEG library in state ?';
22: ss := 'JPEG parameter struct mismatch: library thinks size is ?, caller expects ?';
23: ss := 'Bogus virtual array access';
24: ss := 'Buffer passed to JPEG library is too small';
25: ss := 'Suspension not allowed here';
26: ss := 'CCIR601 sampling not implemented yet';
27: ss := 'Too many color components: ?, max ?';
28: ss := 'Unsupported color conversion request';
29: ss := 'Bogus DAC index ?';
30: ss := 'Bogus DAC value 0x?';
31: ss := 'Bogus DHT index ?';
32: ss := 'Bogus DQT index ?';
33: ss := 'Empty JPEG image (DNL not supported)';
34: ss := 'Read from EMS failed';
35: ss := 'Write to EMS failed';
36: ss := 'Didn''t expect more than one scan';
37: ss := 'Input file read error';
38: ss := 'Output file write error --- out of disk space?';
39: ss := 'Fractional sampling not implemented yet';
40: ss := 'Huffman code size table overflow';
41: ss := 'Missing Huffman code table entry';
42: ss := 'Maximum supported image dimension is ? pixels';
43: ss := 'Empty input file';
44: ss := 'Premature end of input file';
45: ss := 'Cannot transcode due to multiple use of quantization table ?';
46: ss := 'Scan script does not transmit all data';
47: ss := 'Invalid color quantization mode change';
48: ss := 'Not implemented yet';
49: ss := 'Requested feature was omitted at compile time';
50: ss := 'Backing store not supported';
51: ss := 'Huffman table was not defined';
52: ss := 'JPEG datastream contains no image';
53: ss := 'Quantization table was not defined';
54: ss := 'Not a JPEG file: starts with ?';
55: ss := 'Insufficient memory (case ?)';
56: ss := 'Cannot quantize more than ? color components';
57: ss := 'Cannot quantize to fewer than ? colors';
58: ss := 'Cannot quantize to more than ? colors';
59: ss := 'Invalid JPEG file structure: two SOF markers';
60: ss := 'Invalid JPEG file structure: missing SOS marker';
61: ss := 'Unsupported JPEG process: SOF type ?';
62: ss := 'Invalid JPEG file structure: two SOI markers';
63: ss := 'Invalid JPEG file structure: SOS before SOF';
64: ss := 'Failed to create temporary file ?';
65: ss := 'Read failed on temporary file';
66: ss := 'Seek failed on temporary file';
67: ss := 'Write failed on temporary file --- out of disk space?';
68: ss := 'Application transferred too few scanlines';
69: ss := 'Unsupported marker type ?';
70: ss := 'Virtual array controller messed up';
71: ss := 'Image too wide for this implementation';
72: ss := 'Read from XMS failed';
73: ss := 'Write to XMS failed';
74: ss := '';
75: ss := '';
76: ss := 'Caution: quantization tables are too coarse for baseline JPEG';
77: ss := 'Adobe APP14 marker: version ?, flags ?, transform ?';
78: ss := 'Unknown APP0 marker (not JFIF), length ?';
79: ss := 'Unknown APP14 marker (not Adobe), length ?';
80: ss := 'Define Arithmetic Table ?';
81: ss := 'Define Huffman Table ?';
82: ss := 'Define Quantization Table ? precision ?';
83: ss := 'Define Restart Interval ?';
84: ss := 'Freed EMS handle ?';
85: ss := 'Obtained EMS handle ?';
86: ss := 'End Of Image';
87: ss := '?';
88: ss := 'JFIF APP0 marker: version ?, density ?';
89: ss := 'Warning: thumbnail image size does not match data length ?';
90: ss := 'JFIF extension marker: type ?, length ?';
91: ss := 'with ? x ? thumbnail image';
92: ss := 'Miscellaneous marker ?, length ?';
93: ss := 'Unexpected marker ?';
94: ss := '?';
95: ss := 'Quantizing to ? = ?*?*? colors';
96: ss := 'Quantizing to ? colors';
97: ss := 'Selected ? colors for quantization';
98: ss := 'At marker ?, recovery action ?';
99: ss := 'RST?';
100: ss := 'Smoothing not supported with nonstandard sampling ratios';
101: ss := 'Start Of Frame ?: width=?, height=?, components=?';
102: ss := 'Component ?: ?hx?v q=?';
103: ss := 'Start of Image';
104: ss := 'Start Of Scan: ? components';
105: ss := 'Component ?: dc=? ac=?';
106: ss := 'Ss=?, Se=?, Ah=?, Al=?';
107: ss := 'Closed temporary file ?';
108: ss := 'Opened temporary file ?';
109: ss := 'JFIF extension marker: JPEG-compressed thumbnail image, length ?';
110: ss := 'JFIF extension marker: palette thumbnail image, length ?';
111: ss := 'JFIF extension marker: RGB thumbnail image, length ?';
112: ss := 'Unrecognized component IDs ? ? ?, assuming YCbCr';
113: ss := 'Freed XMS handle ?';
114: ss := 'Obtained XMS handle ?';
115: ss := 'Unknown Adobe color transform code ?';
116: ss := 'Inconsistent progression sequence for component ? coefficient ?';
117: ss := 'Corrupt JPEG data: ? extraneous bytes before marker ?';
118: ss := 'Corrupt JPEG data: premature end of data segment';
119: ss := 'Corrupt JPEG data: bad Huffman code';
120: ss := 'Warning: unknown JFIF revision number ?';
121: ss := 'Premature end of JPEG file';
122: ss := 'Corrupt JPEG data: found marker ? instead of RST?';
123: ss := 'Invalid SOS parameters for sequential JPEG';
124: ss := 'Application transferred too many scanlines';
else
ss := 'Corrupted';
end;
if (c <> 68) then
raise EIEJpegException.Create(ss);
end;
procedure EmitMessage(cinfo: j_common_ptr; msg_level: Integer);
begin
if msg_level = -1 then
inc(cinfo^.err^.num_warnings);
end;
procedure OutputMessage(cinfo: j_common_ptr);
begin
//
end;
procedure FormatMessage(cinfo: j_common_ptr; buffer: PAnsiChar);
begin
//
end;
procedure ResetErrorMgr(cinfo: j_common_ptr);
begin
cinfo^.err^.num_warnings := 0;
cinfo^.err^.msg_code := 0;
end;
const
jpeg_std_error: jpeg_error_mgr = (
error_exit: JpegError;
emit_message: EmitMessage;
output_message: OutputMessage;
format_message: FormatMessage;
reset_error_mgr: ResetErrorMgr);
/////////////////////////////////////////////////////////////////////////////////////
// destination manager
const
OUTPUT_BUF_SIZE = 65536;
procedure init_destination(cinfo: J_COMPRESS_PTR);
var
dest: PIEJPEGDESTMGR;
begin
dest := PIEJPEGDESTMGR(cinfo^.dest);
getmem(dest^.buffer, OUTPUT_BUF_SIZE * sizeof(JOCTET));
dest^.pub.next_output_byte := dest^.buffer;
dest^.pub.free_in_buffer := OUTPUT_BUF_SIZE;
end;
function empty_output_buffer(cinfo: J_COMPRESS_PTR): boolean;
var
dest: PIEJPEGDESTMGR;
begin
dest := PIEJPEGDESTMGR(cinfo^.dest);
if dest^.fs.write(pbyte(dest^.buffer)^, OUTPUT_BUF_SIZE) <> OUTPUT_BUF_SIZE then
if assigned(dest^.aborting) then
dest^.aborting^ := true;
dest^.pub.next_output_byte := dest^.buffer;
dest^.pub.free_in_buffer := OUTPUT_BUF_SIZE;
result := true;
end;
procedure term_destination(cinfo: J_COMPRESS_PTR);
var
dest: PIEJPEGDESTMGR;
datacount: integer;
begin
dest := PIEJPEGDESTMGR(cinfo^.dest);
datacount := OUTPUT_BUF_SIZE - dest^.pub.free_in_buffer;
if datacount > 0 then
begin
if dest^.fs.write(pbyte(dest^.buffer)^, datacount) <> datacount then
if assigned(dest^.aborting) then
dest^.aborting^ := true;
end;
end;
/////////////////////////////////////////////////////////////////////////////////////
// source manager
const
INPUT_BUF_SIZE = 65536;
procedure init_source(cinfo: J_DECOMPRESS_PTR);
var
src: PIEJPEGSOURCEMGR;
begin
src := PIEJPEGSOURCEMGR(cinfo^.src);
src^.start_of_file := true;
end;
function fill_input_buffer(cinfo: J_DECOMPRESS_PTR): longbool;
var
src: PIEJPEGSOURCEMGR;
nbytes: integer;
begin
src := PIEJPEGSOURCEMGR(cinfo^.src);
nbytes := src^.fs.Read(pbyte(src^.buffer)^, INPUT_BUF_SIZE);
if nbytes <= 0 then
begin
if src^.start_of_file then
if assigned(src^.aborting) then
src^.aborting^ := true;
pbytearray(src^.buffer)^[0] := $FF;
pbytearray(src^.buffer)^[1] := JPEG_EOI;
nbytes := 2;
end;
src^.pub.next_input_byte := src^.buffer;
src^.pub.bytes_in_buffer := nbytes;
src^.start_of_file := false;
result := true;
end;
procedure skip_input_data(cinfo: J_DECOMPRESS_PTR; num_bytes: integer);
var
src: PIEJPEGSOURCEMGR;
begin
src := PIEJPEGSOURCEMGR(cinfo^.src);
if num_bytes > 0 then
begin
while num_bytes > src^.pub.bytes_in_buffer do
begin
dec(num_bytes, src^.pub.bytes_in_buffer);
fill_input_buffer(cinfo);
end;
inc(src^.pub.next_input_byte, num_bytes);
dec(src^.pub.bytes_in_buffer, num_bytes);
end;
end;
procedure term_source(cinfo: J_DECOMPRESS_PTR);
begin
end;
///////////////////////////////////////////////////////////////////////////
type
IEJPEG_Decomp_Struct = j_decompress_ptr;
IEJPEG_ErrorManager = jpeg_error_mgr_ptr;
IEJPEG_Marker = jpeg_saved_marker_ptr;
IEJPEG_Comp_Struct = j_compress_ptr;
IEJPEG_Transform = j_transform_info_ptr;
function IEJPEG_Decomp_AllocDecompStruct(): IEJPEG_Decomp_Struct;
begin
result := AllocMem( sizeof(jpeg_decompress_struct) );
end;
procedure IEJPEG_Decomp_FreeDecompStruct(var cinfo: IEJPEG_Decomp_Struct);
begin
FreeMem(cinfo);
cinfo := nil;
end;
function IEJPEG_CreateErrorManager(aborting: pboolean): IEJPEG_ErrorManager;
begin
result := AllocMem( sizeof(jpeg_error_mgr) );
result^ := jpeg_std_error;
result^.aborting := aborting;
end;
procedure IEJPEG_FreeErrorManager(var err: IEJPEG_ErrorManager);
begin
FreeMem(err);
err := nil;
end;
procedure IEJPEG_Decomp_SetErrorManager(cinfo: IEJPEG_Decomp_Struct; err: IEJPEG_ErrorManager);
begin
cinfo^.common.err := err;
end;
procedure IEJPEG_DisableAbortings(err: IEJPEG_ErrorManager);
begin
err^.aborting := nil;
end;
function IEJPEG_GetNumWarnings(err: IEJPEG_ErrorManager): integer;
begin
result := err^.num_warnings;
end;
function IEJPEG_GetErrMsgCode(err: IEJPEG_ErrorManager): integer;
begin
result := err^.msg_code;
end;
procedure IEJPEG_Decomp_CreateDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
jpeg_CreateDecompress(cinfo^, JPEG_LIB_VERSION, sizeof(jpeg_decompress_struct));
end;
procedure IEJPEG_Decomp_DestroyDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
jpeg_destroy_decompress(cinfo^);
end;
// replaces jpeg_ie_src
procedure IEJPEG_Decomp_SetupReadStream(cinfo: IEJPEG_Decomp_Struct; stream: TStream; aborting: pboolean);
var
src: PIEJPEGSOURCEMGR;
begin
if cinfo^.src = nil then
begin
getmem(cinfo^.src, sizeof(TIEJPEGSOURCEMGR));
src := PIEJPEGSOURCEMGR(cinfo^.src);
getmem(src^.buffer, INPUT_BUF_SIZE * sizeof(JOCTET));
end;
src := PIEJPEGSOURCEMGR(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^.fs := stream;
src^.pub.bytes_in_buffer := 0;
src^.pub.next_input_byte := nil;
src^.aborting := aborting;
end;
// replaces jpeg_ie_src_free
procedure IEJPEG_Decomp_CleanupReadStream(cinfo: IEJPEG_Decomp_Struct);
var
src: PIEJPEGSOURCEMGR;
begin
src := PIEJPEGSOURCEMGR(cinfo^.src);
if src <> nil then
begin
freemem(src^.buffer);
freemem(cinfo^.src);
end;
end;
procedure IEJPEG_Decomp_ReadHeader(cinfo: IEJPEG_Decomp_Struct; requireImage: boolean);
begin
jpeg_read_header(cinfo^, requireImage);
end;
procedure IEJPEG_Decomp_SaveMarkers(cinfo: IEJPEG_Decomp_Struct; markerCode: integer; lengthLimit: dword);
begin
jpeg_save_markers(cinfo^, markerCode, lengthLimit);
end;
procedure IEJPEG_Decomp_StartDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
jpeg_start_decompress(cinfo^);
end;
function IEJPEG_Decomp_GetOutputWidth(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo^.output_width;
end;
function IEJPEG_Decomp_GetOutputHeight(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo^.output_height;
end;
function IEJPEG_Decomp_GetOutputComponents(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo^.output_components;
end;
function IEJPEG_Decomp_GetOutputScanline(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo^.output_Scanline;
end;
function IEJPEG_Decomp_ReadScanlines(cinfo: IEJPEG_Decomp_Struct; scanlines: pointer; max_lines: cardinal): cardinal;
begin
result := jpeg_read_scanlines(cinfo^, scanlines, max_lines);
end;
procedure IEJPEG_Decomp_FinishDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
jpeg_finish_decompress(cinfo^);
end;
procedure IEJPEG_Decomp_SetOutColorSpace(cinfo: IEJPEG_Decomp_Struct; colorSpace: IEJ_COLOR_SPACE);
begin
cinfo^.out_color_space := colorSpace;
end;
function IEJPEG_Decomp_GetOutColorSpace(cinfo: IEJPEG_Decomp_Struct): IEJ_COLOR_SPACE;
begin
result := cinfo^.out_color_space;
end;
function IEJPEG_Decomp_GetJpegColorSpace(cinfo: IEJPEG_Decomp_Struct): IEJ_COLOR_SPACE;
begin
result := cinfo^.jpeg_color_space;
end;
procedure IEJPEG_Decomp_SetDCTMethod(cinfo: IEJPEG_Decomp_Struct; DCTMethod: IEJ_DCT_METHOD);
begin
cinfo^.dct_method := DCTMethod;
end;
function IEJPEG_Decomp_GetDensityUnit(cinfo: IEJPEG_Decomp_Struct): byte;
begin
result := cinfo^.density_unit;
end;
function IEJPEG_Decomp_GetXDensity(cinfo: IEJPEG_Decomp_Struct): word;
begin
result := cinfo^.X_density;
end;
function IEJPEG_Decomp_GetYDensity(cinfo: IEJPEG_Decomp_Struct): word;
begin
result := cinfo^.Y_density;
end;
function IEJPEG_Decomp_GetProgressiveMode(cinfo: IEJPEG_Decomp_Struct): boolean;
begin
result := cinfo^.progressive_mode;
end;
function IEJPEG_Decomp_GetImageWidth(cinfo: IEJPEG_Decomp_Struct): dword;
begin
result := cinfo^.image_width;
end;
function IEJPEG_Decomp_GetImageHeight(cinfo: IEJPEG_Decomp_Struct): dword;
begin
result := cinfo^.image_height;
end;
procedure IEJPEG_Decomp_SetScale(cinfo: IEJPEG_Decomp_Struct; num, den: dword);
begin
cinfo^.scale_num := num;
cinfo^.scale_denom := den;
end;
function IEJPEG_Decomp_GetMarkerList(cinfo: IEJPEG_Decomp_Struct): IEJPEG_Marker;
begin
result := cinfo^.marker_list; // result is jpeg_saved_marker_ptr
end;
function IEJPEG_GetMarkerID(markerList: IEJPEG_Marker): byte;
begin
result := markerList^.marker;
end;
function IEJPEG_GetMarkerData(markerList: IEJPEG_Marker): pointer;
begin
result := markerList^.data;
end;
function IEJPEG_GetMarkerLength(markerList: IEJPEG_Marker): dword;
begin
result := markerList^.data_length;
end;
function IEJPEG_GetMarkerNext(markerList: IEJPEG_Marker): IEJPEG_Marker;
begin
result := markerList^.next;
end;
function IEJPEG_Comp_AllocCompStruct(): IEJPEG_Comp_Struct;
begin
result := AllocMem( sizeof(jpeg_compress_struct) );
end;
procedure IEJPEG_Comp_FreeCompStruct(var cinfo: IEJPEG_Comp_Struct);
begin
FreeMem(cinfo);
cinfo := nil;
end;
procedure IEJPEG_Comp_SetErrorManager(cinfo: IEJPEG_Comp_Struct; err: IEJPEG_ErrorManager);
begin
cinfo^.common.err := err;
end;
procedure IEJPEG_Comp_CreateCompress(cinfo: IEJPEG_Comp_Struct);
begin
jpeg_CreateCompress(cinfo^, JPEG_LIB_VERSION, sizeof(jpeg_compress_struct));
end;
procedure IEJPEG_Comp_DestroyCompress(cinfo: IEJPEG_Comp_Struct);
begin
jpeg_destroy_compress(cinfo^);
end;
// replaces jpeg_ie_dest
procedure IEJPEG_Comp_SetupWriteStream(cinfo: IEJPEG_Comp_Struct; stream: TStream; aborting: pboolean);
var
dest: PIEJPEGDESTMGR;
begin
if cinfo^.dest = nil then
getmem(cinfo^.dest, sizeof(TIEJPEGDESTMGR));
dest := PIEJPEGDESTMGR(cinfo^.dest);
dest^.pub.init_destination := @init_destination;
dest^.pub.empty_output_buffer := @empty_output_buffer;
dest^.pub.term_destination := @term_destination;
dest^.fs := stream;
dest^.aborting := aborting;
dest^.buffer := nil;
end;
// replaces jpeg_ie_dest_free
procedure IEJPEG_Comp_CleanupWriteStream(cinfo: IEJPEG_Comp_Struct);
var
dest: PIEJPEGDESTMGR;
begin
dest := PIEJPEGDESTMGR(cinfo^.dest);
freemem(dest^.buffer);
freemem(cinfo^.dest);
end;
procedure IEJPEG_Comp_SetImageWidth(cinfo: IEJPEG_Comp_Struct; imageWidth: integer);
begin
cinfo^.image_width := imageWidth;
end;
procedure IEJPEG_Comp_SetImageHeight(cinfo: IEJPEG_Comp_Struct; imageHeight: integer);
begin
cinfo^.image_height := imageHeight;
end;
procedure IEJPEG_Comp_SetInputComponents(cinfo: IEJPEG_Comp_Struct; inputComponents: integer);
begin
cinfo^.input_components := inputComponents;
end;
procedure IEJPEG_Comp_SetInColorSpace(cinfo: IEJPEG_Comp_Struct; colorSpace: IEJ_COLOR_SPACE);
begin
cinfo^.in_color_space := colorSpace;
end;
procedure IEJPEG_Comp_SetColorSpace(cinfo: IEJPEG_Comp_Struct; colorSpace: IEJ_COLOR_SPACE);
begin
jpeg_set_colorspace(cinfo^, colorSpace);
end;
procedure IEJPEG_Comp_SetDefaults(cinfo: IEJPEG_Comp_Struct);
begin
jpeg_set_defaults(cinfo^);
end;
procedure IEJPEG_Comp_SetDensityUnit(cinfo: IEJPEG_Comp_Struct; densityUnit: byte);
begin
cinfo^.density_unit := densityUnit;
end;
procedure IEJPEG_Comp_SetXDensity(cinfo: IEJPEG_Comp_Struct; XDensity: word);
begin
cinfo^.X_density := XDensity;
end;
procedure IEJPEG_Comp_SetYDensity(cinfo: IEJPEG_Comp_Struct; YDensity: word);
begin
cinfo^.Y_density := YDensity;
end;
procedure IEJPEG_Comp_SetDCTMethod(cinfo: IEJPEG_Comp_Struct; DCTMethod: IEJ_DCT_METHOD);
begin
cinfo^.dct_method := DCTMethod;
end;
procedure IEJPEG_Comp_SetOptimizeCoding(cinfo: IEJPEG_Comp_Struct; optimizeCoding: boolean);
begin
cinfo^.optimize_coding := optimizeCoding;
end;
procedure IEJPEG_Comp_SetSmoothingFactor(cinfo: IEJPEG_Comp_Struct; smoothingFactor: integer);
begin
cinfo^.smoothing_factor := smoothingFactor;
end;
procedure IEJPEG_Comp_SetQuality(cinfo: IEJPEG_Comp_Struct; quality: integer; baseline: boolean);
begin
jpeg_set_quality(cinfo^, quality, baseline);
end;
procedure IEJPEG_Comp_SetSampleFactor(cinfo: IEJPEG_Comp_Struct; componentIndex: integer; H: integer; V: integer);
var
comp: jpeg_component_info_ptr;
begin
comp := cinfo^.comp_info;
inc(comp, componentIndex);
comp^.h_samp_factor := H;
comp^.v_samp_factor := V;
end;
procedure IEJPEG_Comp_SetWriteJFIFHeader(cinfo: IEJPEG_Comp_Struct; writeJFIFHeader: boolean);
begin
cinfo^.write_JFIF_header := writeJFIFHeader;
end;
procedure IEJPEG_Comp_SimpleProgression(cinfo: IEJPEG_Comp_Struct);
begin
jpeg_simple_progression(cinfo^);
end;
procedure IEJPEG_Comp_StartCompress(cinfo: IEJPEG_Comp_Struct; writeAllTables: boolean);
begin
jpeg_start_compress(cinfo^, writeAllTables);
end;
procedure IEJPEG_Comp_WriteMarker(cinfo: IEJPEG_Comp_Struct; markerID: integer; markerPtr: pointer; markerLength: dword);
begin
jpeg_write_marker(cinfo^, markerID, markerPtr, markerLength);
end;
function IEJPEG_Comp_GetNextScanlineIndex(cinfo: IEJPEG_Comp_Struct): dword;
begin
result := cinfo^.next_scanline;
end;
function IEJPEG_Comp_WriteScanlines(cinfo: IEJPEG_Comp_Struct; scanlines: pointer; maxLines: dword): dword;
begin
result := jpeg_write_scanlines(cinfo^, scanlines, maxLines);
end;
procedure IEJPEG_Comp_FinishCompress(cinfo: IEJPEG_Comp_Struct);
begin
jpeg_finish_compress(cinfo^);
end;
procedure IEJPEG_JCopyMarkersSetup(sourceInfo: IEJPEG_Decomp_Struct; option: integer);
begin
jcopy_markers_setup(sourceInfo^, option);
end;
function IEJPEG_CreateTransformOption(transform: integer; trim: boolean; forceGrayscale: boolean; xoffs: dword; yoffs: dword; newWidth: dword; newHeight: dword): IEJPEG_Transform;
begin
result := AllocMem( sizeof(jpeg_transform_info) );
result^.transform := transform;
result^.trim := ord(trim);
result^.force_grayscale := ord(forceGrayscale);
result^.xoffs := xoffs;
result^.yoffs := yoffs;
result^.newwidth := newWidth;
result^.newheight := newHeight;
end;
procedure IEJPEG_FreeTransformOption(var toption: IEJPEG_Transform);
begin
FreeMem(toption);
toption := nil;
end;
procedure IEJPEG_JTransformRequestWorkspace(sourceInfo: IEJPEG_Decomp_Struct; transform: IEJPEG_Transform);
begin
jtransform_request_workspace(sourceInfo^, transform^);
end;
function IEJPEG_ReadCoefficients(sourceInfo: IEJPEG_Decomp_Struct): pointer;
begin
result := jpeg_read_coefficients(sourceInfo^);
end;
procedure IEJPEG_CopyCritialParameters(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct);
begin
jpeg_copy_critical_parameters(sourceInfo^, destInfo^);
end;
function IEJPEG_JTransformAdjustParameters(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct; sourceCoefArrays: pointer; transform: IEJPEG_Transform): pointer;
begin
result := jtransform_adjust_parameters(sourceInfo^, destInfo^, sourceCoefArrays, transform^);
end;
procedure IEJPEG_WriteCoefficients(destInfo: IEJPEG_Comp_Struct; destCoefArrays: pointer);
begin
jpeg_write_coefficients(destInfo^, destCoefArrays);
end;
procedure IEJPEG_JCopyMarkersExecute(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct; copyOption: integer);
begin
jcopy_markers_execute(sourceInfo^, destInfo^, copyOption);
end;
procedure IEJPEG_JTransformExecuteTransformation(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct; sourceCoefArrays: pointer; transform: IEJPEG_Transform);
begin
jtransform_execute_transformation(sourceInfo^, destInfo^, sourceCoefArrays, transform^);
end;
{$endif} // IEUSEDLLJPEGLIB
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// dynamic (DLL) jpeg library wrappers
{$ifdef IEUSEDLLJPEGLIB}
type
IEJPEG_Decomp_Struct = TIELibJPEGDecompressor;
IEJPEG_ErrorManager = TIELibJPEGErrorManager;
IEJPEG_Marker = TIELibJPEGMarker;
IEJPEG_Comp_Struct = TIELibJPEGCompressor;
IEJPEG_Transform = TIELibJPEGTransform;
function IEJPEG_Decomp_AllocDecompStruct(): IEJPEG_Decomp_Struct;
begin
if IELibAvailable() then
result := IELib.createJPEGDecompressor()
else
raise EIEJpegException.Create(IERS_IEVISIONNOTFOUND);
end;
procedure IEJPEG_Decomp_FreeDecompStruct(var cinfo: IEJPEG_Decomp_Struct);
begin
cinfo := nil;
end;
function IEJPEG_CreateErrorManager(aborting: pboolean): IEJPEG_ErrorManager;
begin
if IELibAvailable() then
result := IELib.createJPEGErrorManager()
else
raise EIEJpegException.Create(IERS_IEVISIONNOTFOUND);
end;
procedure IEJPEG_FreeErrorManager(var err: IEJPEG_ErrorManager);
begin
err := nil;
end;
procedure IEJPEG_Decomp_SetErrorManager(cinfo: IEJPEG_Decomp_Struct; err: IEJPEG_ErrorManager);
begin
cinfo.setErrorManager(err);
end;
procedure IEJPEG_DisableAbortings(err: IEJPEG_ErrorManager);
begin
// todo
end;
function IEJPEG_GetNumWarnings(err: IEJPEG_ErrorManager): integer;
begin
result := err.getNumWarnings();
end;
function IEJPEG_GetErrMsgCode(err: IEJPEG_ErrorManager): integer;
begin
result := err.getErrMsgCode();
end;
procedure IEJPEG_Decomp_CreateDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
cinfo.createDecompress();
end;
procedure IEJPEG_Decomp_DestroyDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
cinfo.destroyDecompress();
end;
procedure IEJPEG_Decomp_SetupReadStream(cinfo: IEJPEG_Decomp_Struct; stream: TStream; aborting: pboolean);
begin
cinfo.setupReadStream(IELib.createCustomStream(TIEVCLStreamProvider.Create(stream)), 65536); // TIEVCLStreamProvider will be freed by IEJPEG_Decomp_Struct (that is TIELibJPEGDecompressor)
end;
procedure IEJPEG_Decomp_CleanupReadStream(cinfo: IEJPEG_Decomp_Struct);
begin
// nothing to do
end;
procedure IEJPEG_Decomp_ReadHeader(cinfo: IEJPEG_Decomp_Struct; requireImage: boolean);
begin
cinfo.readHeader(requireImage);
end;
procedure IEJPEG_Decomp_SaveMarkers(cinfo: IEJPEG_Decomp_Struct; markerCode: integer; lengthLimit: dword);
begin
cinfo.saveMarkers(markerCode, lengthLimit);
end;
procedure IEJPEG_Decomp_StartDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
cinfo.startDecompress();
end;
function IEJPEG_Decomp_GetOutputWidth(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo.getOutputWidth();
end;
function IEJPEG_Decomp_GetOutputHeight(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo.getOutputHeight();
end;
function IEJPEG_Decomp_GetOutputComponents(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo.getOutputComponents();
end;
function IEJPEG_Decomp_GetOutputScanline(cinfo: IEJPEG_Decomp_Struct): integer;
begin
result := cinfo.getOutputScanline();
end;
function IEJPEG_Decomp_ReadScanlines(cinfo: IEJPEG_Decomp_Struct; scanlines: pointer; max_lines: cardinal): cardinal;
begin
result := cinfo.readScanlines(scanlines, max_lines);
end;
procedure IEJPEG_Decomp_FinishDecompress(cinfo: IEJPEG_Decomp_Struct);
begin
cinfo.finishDecompress();
end;
procedure IEJPEG_Decomp_SetOutColorSpace(cinfo: IEJPEG_Decomp_Struct; colorSpace: IEJ_COLOR_SPACE);
begin
cinfo.setOutColorSpace(dword(colorSpace));
end;
function IEJPEG_Decomp_GetOutColorSpace(cinfo: IEJPEG_Decomp_Struct): IEJ_COLOR_SPACE;
begin
result := IEJ_COLOR_SPACE(cinfo.getOutColorSpace());
end;
function IEJPEG_Decomp_GetJpegColorSpace(cinfo: IEJPEG_Decomp_Struct): IEJ_COLOR_SPACE;
begin
result := IEJ_COLOR_SPACE(cinfo.getJpegColorSpace());
end;
procedure IEJPEG_Decomp_SetDCTMethod(cinfo: IEJPEG_Decomp_Struct; DCTMethod: IEJ_DCT_METHOD);
begin
cinfo.setDCTMethod(dword(DCTMethod));
end;
function IEJPEG_Decomp_GetDensityUnit(cinfo: IEJPEG_Decomp_Struct): byte;
begin
result := cinfo.getDensityUnit();
end;
function IEJPEG_Decomp_GetXDensity(cinfo: IEJPEG_Decomp_Struct): word;
begin
result := cinfo.getXDensity();
end;
function IEJPEG_Decomp_GetYDensity(cinfo: IEJPEG_Decomp_Struct): word;
begin
result := cinfo.getYDensity();
end;
function IEJPEG_Decomp_GetProgressiveMode(cinfo: IEJPEG_Decomp_Struct): boolean;
begin
result := cinfo.getProgressiveMode();
end;
function IEJPEG_Decomp_GetImageWidth(cinfo: IEJPEG_Decomp_Struct): dword;
begin
result := cinfo.getImageWidth();
end;
function IEJPEG_Decomp_GetImageHeight(cinfo: IEJPEG_Decomp_Struct): dword;
begin
result := cinfo.getImageHeight();
end;
procedure IEJPEG_Decomp_SetScale(cinfo: IEJPEG_Decomp_Struct; num, den: dword);
begin
cinfo.setScale(num, den);
end;
function IEJPEG_Decomp_GetMarkerList(cinfo: IEJPEG_Decomp_Struct): IEJPEG_Marker;
begin
result := cinfo.getMarkerList();
end;
function IEJPEG_GetMarkerID(markerList: IEJPEG_Marker): byte;
begin
result := markerList.getID();
end;
function IEJPEG_GetMarkerData(markerList: IEJPEG_Marker): pointer;
begin
result := markerList.getData();
end;
function IEJPEG_GetMarkerLength(markerList: IEJPEG_Marker): dword;
begin
result := markerList.getLength();
end;
function IEJPEG_GetMarkerNext(markerList: IEJPEG_Marker): IEJPEG_Marker;
begin
result := markerList.next();
end;
function IEJPEG_Comp_AllocCompStruct(): IEJPEG_Comp_Struct;
begin
if IELibAvailable() then
result := IELib.createJPEGCompressor()
else
raise EIEJpegException.Create(IERS_IEVISIONNOTFOUND);
end;
procedure IEJPEG_Comp_FreeCompStruct(var cinfo: IEJPEG_Comp_Struct);
begin
cinfo := nil;
end;
procedure IEJPEG_Comp_SetErrorManager(cinfo: IEJPEG_Comp_Struct; err: IEJPEG_ErrorManager);
begin
cinfo.setErrorManager(err);
end;
procedure IEJPEG_Comp_CreateCompress(cinfo: IEJPEG_Comp_Struct);
begin
cinfo.createCompress();
end;
procedure IEJPEG_Comp_DestroyCompress(cinfo: IEJPEG_Comp_Struct);
begin
cinfo.destroyCompress();
end;
procedure IEJPEG_Comp_SetupWriteStream(cinfo: IEJPEG_Comp_Struct; stream: TStream; aborting: pboolean);
begin
cinfo.setupWriteStream(IELib.createCustomStream(TIEVCLStreamProvider.Create(stream)), 65536); // TIEVCLStreamProvider will be freed by IEJPEG_Comp_Struct (that is TIELibJPEGCompressor)
end;
procedure IEJPEG_Comp_CleanupWriteStream(cinfo: IEJPEG_Comp_Struct);
begin
// nothing to do
end;
procedure IEJPEG_Comp_SetImageWidth(cinfo: IEJPEG_Comp_Struct; imageWidth: integer);
begin
cinfo.setImageWidth(imageWidth);
end;
procedure IEJPEG_Comp_SetImageHeight(cinfo: IEJPEG_Comp_Struct; imageHeight: integer);
begin
cinfo.setImageHeight(imageHeight);
end;
procedure IEJPEG_Comp_SetInputComponents(cinfo: IEJPEG_Comp_Struct; inputComponents: integer);
begin
cinfo.setInputComponents(inputComponents);
end;
procedure IEJPEG_Comp_SetInColorSpace(cinfo: IEJPEG_Comp_Struct; colorSpace: IEJ_COLOR_SPACE);
begin
cinfo.setInColorSpace(dword(colorSpace));
end;
procedure IEJPEG_Comp_SetColorSpace(cinfo: IEJPEG_Comp_Struct; colorSpace: IEJ_COLOR_SPACE);
begin
cinfo.setColorSpace(dword(colorSpace));
end;
procedure IEJPEG_Comp_SetDefaults(cinfo: IEJPEG_Comp_Struct);
begin
cinfo.setDefaults();
end;
procedure IEJPEG_Comp_SetDensityUnit(cinfo: IEJPEG_Comp_Struct; densityUnit: byte);
begin
cinfo.setDensityUnit(densityUnit);
end;
procedure IEJPEG_Comp_SetXDensity(cinfo: IEJPEG_Comp_Struct; XDensity: word);
begin
cinfo.setXDensity(XDensity);
end;
procedure IEJPEG_Comp_SetYDensity(cinfo: IEJPEG_Comp_Struct; YDensity: word);
begin
cinfo.setYDensity(YDensity);
end;
procedure IEJPEG_Comp_SetDCTMethod(cinfo: IEJPEG_Comp_Struct; DCTMethod: IEJ_DCT_METHOD);
begin
cinfo.setDCTMethod(dword(DCTMethod));
end;
procedure IEJPEG_Comp_SetOptimizeCoding(cinfo: IEJPEG_Comp_Struct; optimizeCoding: boolean);
begin
cinfo.setOptimizeCoding(optimizeCoding);
end;
procedure IEJPEG_Comp_SetSmoothingFactor(cinfo: IEJPEG_Comp_Struct; smoothingFactor: integer);
begin
cinfo.setSmoothingFactor(smoothingFactor);
end;
procedure IEJPEG_Comp_SetQuality(cinfo: IEJPEG_Comp_Struct; quality: integer; baseline: boolean);
begin
cinfo.setQuality(quality, baseline);
end;
procedure IEJPEG_Comp_SetSampleFactor(cinfo: IEJPEG_Comp_Struct; componentIndex: integer; H: integer; V: integer);
begin
cinfo.setSampleFactor(componentIndex, H, V);
end;
procedure IEJPEG_Comp_SetWriteJFIFHeader(cinfo: IEJPEG_Comp_Struct; writeJFIFHeader: boolean);
begin
cinfo.setWriteJFIFHeader(writeJFIFHeader);
end;
procedure IEJPEG_Comp_SimpleProgression(cinfo: IEJPEG_Comp_Struct);
begin
cinfo.simpleProgression();
end;
procedure IEJPEG_Comp_StartCompress(cinfo: IEJPEG_Comp_Struct; writeAllTables: boolean);
begin
cinfo.startCompress(writeAllTables);
end;
procedure IEJPEG_Comp_WriteMarker(cinfo: IEJPEG_Comp_Struct; markerID: integer; markerPtr: pointer; markerLength: dword);
begin
cinfo.writeMarker(markerID, markerPtr, markerLength);
end;
function IEJPEG_Comp_GetNextScanlineIndex(cinfo: IEJPEG_Comp_Struct): dword;
begin
result := cinfo.getNextScanlineIndex();
end;
function IEJPEG_Comp_WriteScanlines(cinfo: IEJPEG_Comp_Struct; scanlines: pointer; maxLines: dword): dword;
begin
result := cinfo.writeScanlines(scanlines, maxLines);
end;
procedure IEJPEG_Comp_FinishCompress(cinfo: IEJPEG_Comp_Struct);
begin
cinfo.finishCompress();
end;
procedure IEJPEG_JCopyMarkersSetup(sourceInfo: IEJPEG_Decomp_Struct; option: integer);
begin
IELib.createJPEGCopy().copyMarkersSetup(sourceInfo, option);
end;
function IEJPEG_CreateTransformOption(transform: integer; trim: boolean; forceGrayscale: boolean; xoffs: dword; yoffs: dword; newWidth: dword; newHeight: dword): IEJPEG_Transform;
begin
result := IELib.createJPEGTransform(transform, trim, forceGrayscale, xoffs, yoffs, newWidth, newHeight);
end;
procedure IEJPEG_FreeTransformOption(var toption: IEJPEG_Transform);
begin
toption := nil;
end;
procedure IEJPEG_JTransformRequestWorkspace(sourceInfo: IEJPEG_Decomp_Struct; transform: IEJPEG_Transform);
begin
transform.requestWorkspace(sourceInfo);
end;
function IEJPEG_ReadCoefficients(sourceInfo: IEJPEG_Decomp_Struct): pointer;
begin
result := IELib.createJPEGCopy().readCoefficients(sourceInfo);
end;
procedure IEJPEG_CopyCritialParameters(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct);
begin
IELib.createJPEGCopy().copyCriticalParameters(sourceInfo, destInfo);
end;
function IEJPEG_JTransformAdjustParameters(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct; sourceCoefArrays: pointer; transform: IEJPEG_Transform): pointer;
begin
result := transform.adjustParameters(sourceInfo, destInfo, sourceCoefArrays);
end;
procedure IEJPEG_WriteCoefficients(destInfo: IEJPEG_Comp_Struct; destCoefArrays: pointer);
begin
IELib.createJPEGCopy().writeCoefficients(destInfo, destCoefArrays);
end;
procedure IEJPEG_JCopyMarkersExecute(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct; copyOption: integer);
begin
IELib.createJPEGCopy().copyMarkersExecute(sourceInfo, destInfo, copyOption);
end;
procedure IEJPEG_JTransformExecuteTransformation(sourceInfo: IEJPEG_Decomp_Struct; destInfo: IEJPEG_Comp_Struct; sourceCoefArrays: pointer; transform: IEJPEG_Transform);
begin
transform.executeTransformation(sourceInfo, destInfo, sourceCoefArrays);
end;
{$endif} // IEUSEDLLJPEGLIB
// end of dynamic (DLL) jpeg library wrappers
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// read stream jpeg
// TableStream contains Q tables. It is nil if all is inside Stream.
// Raw=true doesn't convert to RGB but get the original color format
// returns number of rows read
function ReadJpegStream(Stream: TStream; TableStream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var xProgress: TProgressRec; Preview: boolean; Raw: boolean; ReadMetaTags: boolean; invertCMYK: boolean; freeICC: boolean; loadicc: boolean; MaxRows: integer; NativeFormat: boolean): integer;
const
FAKE_EXIF_THUMB_SIZE = 300; // The min X and Y dimensions to create an image if user has set JPEG_GetExifThumbnail, but no EXIF thumb is available
var
cinfo: IEJPEG_Decomp_Struct;
jerr: IEJPEG_ErrorManager;
DestScanLine: pointer;
LinesRead: integer;
buff: pbyte;
pb: pbyte;
xrgb: PRGB;
bo: boolean;
bst, dv, i: integer;
markers: IEJPEG_Marker;
spos: int64;
mi: integer;
dd: double;
HasICC: boolean;
xdpi, ydpi: integer;
oWidth, oHeight: integer;
lper: integer;
scale: dword;
outputWidth, outputHeight: integer;
adjustOrientation: boolean;
dummy: Boolean;
begin
result := 0;
{$ifdef IEUSEDLLJPEGLIB}
if not IELibAvailable() then
raise EIEJpegException.Create(IERS_IEVISIONNOTFOUND);
{$endif}
lper := -1;
adjustOrientation := false;
if IOParams.JPEG_GetExifThumbnail then
begin
// try to load the EXIF thumbnail
if IOParams.EXIF_Bitmap <> nil then
IOParams.EXIF_Bitmap.FreeImage(true);
IOParams.JPEG_GetExifThumbnail := false;
oWidth := IOParams.Width; // save requested width because it is changed by ReadJpegStream
oHeight := IOParams.Height;
ReadJpegStream(Stream, TableStream, Bitmap, IOParams, xProgress, true, Raw, true,
invertCMYK, freeICC, false, -1, IOParams.IsNativePixelFormat);
Stream.Position := 0;
IOParams.JPEG_GetExifThumbnail := true;
if assigned(IOParams.EXIF_Bitmap) and not IOParams.EXIF_Bitmap.IsEmpty then
begin
Bitmap.Assign( IOParams.EXIF_Bitmap ); // thumbnails should be already oriented
exit;
end;
if IOParams.JPEG_SCALE = ioJPEG_FULLSIZE then
begin
// Load at 1/8 size as "Makeshift" thumbnail
Stream.Position := 0;
IOParams.Width := FAKE_EXIF_THUMB_SIZE;
IOParams.Height := FAKE_EXIF_THUMB_SIZE;
IOParams.JPEG_SCALE := ioJPEG_AUTOCALC;
IOParams.JPEG_GetExifThumbnail := false;
ReadJpegStream(Stream, TableStream, Bitmap, IOParams, xProgress, False, Raw, true,
invertCMYK, freeICC, false, -1, IOParams.IsNativePixelFormat);
IOParams.JPEG_SCALE := ioJPEG_FULLSIZE;
IOParams.Width := oWidth;
IOParams.Height := oHeight;
IOParams.JPEG_GetExifThumbnail := True;
exit;
end;
Stream.Position := 0;
IOParams.Width := oWidth;
IOParams.Height := oHeight;
IOParams.OriginalWidth := oWidth;
IOParams.OriginalHeight := oHeight;
end;
IOParams.JPEG_WarningTot := 0;
IOParams.JPEG_WarningCode := 0;
spos := JpegTryStream(Stream, true);
if spos = -1 then
begin
xProgress.Aborting^ := true;
exit;
end;
Stream.Position := spos;
// load ICC, if present
if loadicc then
IEGetJpegICC(Stream, IOParams);
hasICC := false;
cinfo := nil;
jerr := nil;
try
buff := nil;
cinfo := IEJPEG_Decomp_AllocDecompStruct();
jerr := IEJPEG_CreateErrorManager(xProgress.Aborting);
IEJPEG_Decomp_SetErrorManager(cinfo, jerr);
IEJPEG_Decomp_CreateDecompress(cinfo);
if xProgress.Aborting^ then
exit;
if (TableStream <> nil) and (TableStream <> Stream) and not Preview then
begin
// load tables from TableStream
IEJPEG_Decomp_SetupReadStream(cinfo, TableStream, xProgress.Aborting);
IEJPEG_Decomp_ReadHeader(cinfo, false);
end;
IEJPEG_Decomp_SetupReadStream(cinfo, Stream, xProgress.Aborting);
if xProgress.Aborting^ then
exit;
try
IEJPEG_Decomp_SaveMarkers(cinfo, JPEG_COM, $FFFF);
for i := JPEG_APP0 to JPEG_APP15 do
IEJPEG_Decomp_SaveMarkers(cinfo, i, $FFFF);
IEJPEG_Decomp_ReadHeader(cinfo, true);
except
xProgress.Aborting^ := true;
exit;
end;
case IEJPEG_Decomp_GetDensityUnit(cinfo) of
0, 1: // unknown or inches
begin
IOParams.DpiX := IEJPEG_Decomp_GetXDensity(cinfo);
IOParams.DpiY := IEJPEG_Decomp_GetYDensity(cinfo);
end;
2: // centimeters
begin
IOParams.DpiX := trunc(IEJPEG_Decomp_GetXDensity(cinfo) / CM_per_Inch);
IOParams.DpiY := trunc(IEJPEG_Decomp_GetYDensity(cinfo) / CM_per_Inch);
end;
end;
// save markers to IOParams.JPEG_MarkerList
IOParams.JPEG_MarkerList.Clear;
markers := IEJPEG_Decomp_GetMarkerList(cinfo);
bo := false;
while markers <> nil do
begin
if (IEJPEG_GetMarkerID(markers) <> JPEG_APP0) or bo then // this remove first APP0 (the header + RGB thumbnail)
IOParams.JPEG_MarkerList.AddMarker(IEJPEG_GetMarkerID(markers), PAnsiChar(IEJPEG_GetMarkerData(markers)), IEJPEG_GetMarkerLength(markers));
if IEJPEG_GetMarkerID(markers) = JPEG_APP0 then
bo := true;
markers := IEJPEG_GetMarkerNext(markers);
end;
// read IPTC, EXIF, XMP, ImageEnAnnot
if ReadMetaTags then
begin
with IOParams.JPEG_MarkerList do
begin
// IPTC
for i := 0 to Count - 1 do
if MarkerType[i] = JPEG_APP13 then
if IOParams.IPTC_Info.LoadFromStandardBuffer(MarkerData[i], MarkerLength[i]) then
break;
// EXIF
// we save current DPI because LoadEXIFFromStandardBuffer will change them
xdpi := IOParams.DpiX;
ydpi := IOParams.DpiY;
for i := 0 to Count - 1 do
if (MarkerType[i] = JPEG_APP1) and LoadEXIFFromStandardBuffer(MarkerData[i], MarkerLength[i], IOParams) then
begin
// exif found
if (xdpi = 1) and (ydpi = 1) then
begin
// use dpi of the Exif
if IOParams.EXIF_ResolutionUnit = 3 then
dd := 2.54
else
dd := 1;
IOParams.DpiX := trunc(IOParams.EXIF_XResolution * dd);
IOParams.DpiY := trunc(IOParams.EXIF_YResolution * dd);
end
else
begin
IOParams.DpiX := xdpi;
IOParams.DpiY := ydpi;
end;
//IEChangeYCbCrCoefficients(Bitmap, 0.299, 0.587, 0.114, IOParams.EXIF_YCbCrCoefficients[0], IOParams.EXIF_YCbCrCoefficients[1], IOParams.EXIF_YCbCrCoefficients[2]);
if IOParams.JPEG_EnableAdjustOrientation then
begin
adjustOrientation := true;
end;
break; // exit loop
end;
// XMP
for mi := 0 to Count - 1 do
if (MarkerType[mi] = JPEG_APP1) and (IEFindXMPFromJpegTag(MarkerData[mi], MarkerLength[mi]) <> nil) then
begin
IELoadXMPFromJpegTag(MarkerData[mi], MarkerLength[mi], IOParams);
break;
end;
// ImageEn annotations (TImageEnVect objects)
for mi := 0 to Count -1 do
if (MarkerType[mi] = JPEG_APP1) and TIEImageEnAnnot.BufferContainsImageEnAnnot(MarkerData[mi], MarkerLength[mi], dummy) then
begin
IOParams.ImageEnAnnot.LoadFromBuffer(MarkerData[mi], MarkerLength[mi]);
break;
end;
end;
end;
// color profile: check for interoperability EXIF
if (IOParams.EXIF_InteropIndex = 'R03') and
not assigned(IOParams.fInputICC) and
((IEGlobalSettings().EXIFInteroperabilityIndexUsage = ieiiApplyOnly) or (IEGlobalSettings().EXIFInteroperabilityIndexUsage = ieiiApplyAndReset))
then
begin
IOParams.InputICCProfile.Assign_AdobeRGB1998();
end;
HasICC := assigned(IOParams) and assigned(IOParams.fInputICC) and IEGlobalSettings().EnableCMS;
if xProgress.Aborting^ then
exit;
IOParams.ImageCount := 1;
IOParams.BitsPerSample := 8;
IEJPEG_Decomp_SetOutColorSpace(cinfo, IEJPEG_Decomp_GetJpegColorSpace(cinfo));
case IEJPEG_Decomp_GetJpegColorSpace(cinfo) of
IEJCS_RGB:
begin
IOParams.JPEG_ColorSpace := ioJPEG_RGB;
IOParams.SamplesPerPixel := 3;
end;
IEJCS_GRAYSCALE:
begin
IOParams.JPEG_ColorSpace := ioJPEG_GRAYLEV;
IOParams.SamplesPerPixel := 1;
end;
IEJCS_YCbCr:
begin
IOParams.JPEG_ColorSpace := ioJPEG_YCbCr;
IOParams.SamplesPerPixel := 3;
if not Raw then
IEJPEG_Decomp_SetOutColorSpace(cinfo, IEJCS_RGB);
end;
IEJCS_CMYK:
begin
IOParams.JPEG_ColorSpace := ioJPEG_CMYK;
IOParams.SamplesPerPixel := 4;
end;
IEJCS_YCCK:
begin
IOParams.JPEG_ColorSpace := ioJPEG_YCbCrK;
IOParams.SamplesPerPixel := 4;
IEJPEG_Decomp_SetOutColorSpace(cinfo, IEJCS_CMYK);
end;
end;
case IOParams.JPEG_DCTMethod of
ioJPEG_ISLOW: IEJPEG_Decomp_SetDCTMethod(cinfo, IEJDCT_ISLOW);
ioJPEG_IFAST: IEJPEG_Decomp_SetDCTMethod(cinfo, IEJDCT_IFAST);
ioJPEG_FLOAT: IEJPEG_Decomp_SetDCTMethod(cinfo, IEJDCT_FLOAT);
end;
IOParams.FreeColorMap;
IOParams.JPEG_Progressive := IEJPEG_Decomp_GetProgressiveMode(cinfo);
IOParams.OriginalWidth := IEJPEG_Decomp_GetImageWidth(cinfo);
IOParams.OriginalHeight := IEJPEG_Decomp_GetImageHeight(cinfo);
scale := 1;
case IOParams.JPEG_Scale of
ioJPEG_FULLSIZE: scale := 1;
ioJPEG_HALF: scale := 2;
ioJPEG_QUARTER: scale := 4;
ioJPEG_EIGHTH: scale := 8;
ioJPEG_AUTOCALC:
begin
// Calc from IOParams.Width and IOParmas.Height
if (IOParams.Width > 0) and (IOParams.Height > 0) then
begin
bst := 0;
for i := 3 downto 0 do
begin
dv := 1 shl i;
if ((integer( IEJPEG_Decomp_GetImageWidth(cinfo) ) div dv) >= IOParams.Width) and ((integer( IEJPEG_Decomp_GetImageHeight(cinfo) ) div dv) >= IOParams.Height) then
begin
bst := i;
break;
end;
end;
scale := 1 shl bst;
end
else
scale := 1;
end;
end;
IEJPEG_Decomp_SetScale(cinfo, 1, scale);
IOParams.JPEG_Scale_Used := scale;
if Preview then
begin
IOParams.Width := IEJPEG_Decomp_GetImageWidth(cinfo);
IOParams.Height := IEJPEG_Decomp_GetImageHeight(cinfo);
exit;
end;
try
IEJPEG_Decomp_StartDecompress(cinfo);
except
xProgress.Aborting^ := true;
exit;
end;
if xProgress.Aborting^ then
exit;
IOParams.Width := IEJPEG_Decomp_GetOutputWidth(cinfo);
IOParams.Height := IEJPEG_Decomp_GetOutputHeight(cinfo);
getmem(buff, IEJPEG_Decomp_GetOutputWidth(cinfo) * 4);
if NativeFormat then
begin
case IEJPEG_Decomp_GetOutColorSpace(cinfo) of
IEJCS_GRAYSCALE: Bitmap.Allocate(IEJPEG_Decomp_GetOutputWidth(cinfo), IEJPEG_Decomp_GetOutputHeight(cinfo), ie8g);
IEJCS_RGB: Bitmap.Allocate(IEJPEG_Decomp_GetOutputWidth(cinfo), IEJPEG_Decomp_GetOutputHeight(cinfo), ie24RGB);
IEJCS_YCbCr: Bitmap.Allocate(IEJPEG_Decomp_GetOutputWidth(cinfo), IEJPEG_Decomp_GetOutputHeight(cinfo), ie24RGB);
IEJCS_CMYK: Bitmap.Allocate(IEJPEG_Decomp_GetOutputWidth(cinfo), IEJPEG_Decomp_GetOutputHeight(cinfo), ieCMYK);
IEJCS_YCCK: Bitmap.Allocate(IEJPEG_Decomp_GetOutputWidth(cinfo), IEJPEG_Decomp_GetOutputHeight(cinfo), ie24RGB);
end;
end
else
Bitmap.Allocate(IEJPEG_Decomp_GetOutputWidth(cinfo), IEJPEG_Decomp_GetOutputHeight(cinfo), ie24RGB);
if MaxRows<0 then
MaxRows := Bitmap.Height;
outputWidth := IEJPEG_Decomp_GetOutputWidth(cinfo);
outputHeight := IEJPEG_Decomp_GetOutputHeight(cinfo);
xProgress.per1 := 100 / outputHeight;
xProgress.val := 0;
try
result := 0;
LinesRead := 1;
while (IEJPEG_Decomp_GetOutputScanline(cinfo) < outputHeight) and (result < MaxRows) do
begin
DestScanline := Bitmap.Scanline[result];
if DestScanLine = nil then
break; // In case a Virtual Bitmap cannot give us a ScanLine
case IEJPEG_Decomp_GetOutColorSpace(cinfo) of
IEJCS_GRAYSCALE:
begin
if Bitmap.PixelFormat=ie8g then
LinesRead := IEJPEG_Decomp_ReadScanlines(cinfo, @DestScanline, 1)
else
begin
LinesRead := IEJPEG_Decomp_ReadScanlines(cinfo, @buff, 1);
xrgb := DestScanline;
pb := buff;
for i := 0 to outputWidth - 1 do
begin
with xrgb^ do
begin
r := pb^;
g := pb^;
b := pb^;
end;
inc(pb);
inc(xrgb);
end;
end;
end;
IEJCS_YCCK:
begin
// this should be never happens because YCCK is converted to CMYK
end;
IEJCS_YCbCr:
begin
if Raw then
begin
LinesRead := IEJPEG_Decomp_ReadScanlines(cinfo, @DestScanline, 1);
if IEJPEG_Decomp_GetJpegColorSpace(cinfo) = IEJCS_RGB then
_BGR2RGB(PRGB(DestScanline), Bitmap.Width);
end;
end;
IEJCS_CMYK:
begin
if Bitmap.PixelFormat = ie24RGB then
begin
// convert CMYK->RGB
LinesRead := IEJPEG_Decomp_ReadScanlines(cinfo, @buff, 1);
if xProgress.Aborting^ then
break;
if invertCMYK then
begin
pb := buff;
for i := 0 to (outputWidth - 1) * 4 do
begin
pb^ := 255-pb^;
inc(pb);
end;
end;
IEGlobalSettings().ConvertColorFunction(buff, iecmsCMYK, DestScanline, iecmsBGR, outputWidth, IOParams);
end
else
begin
// native CMYK format
LinesRead := IEJPEG_Decomp_ReadScanlines(cinfo, @DestScanline, 1);
if xProgress.Aborting^ then
break;
end;
end;
IEJCS_RGB:
begin
if HasICC then
begin
LinesRead := IEJPEG_Decomp_ReadScanlines(cinfo, @buff, 1);
if IEJPEG_Decomp_GetJpegColorSpace(cinfo) = IEJCS_RGB then
IEGlobalSettings().ConvertColorFunction(buff, iecmsRGB, DestScanline, iecmsBGR, outputWidth, IOParams)
else
IEGlobalSettings().ConvertColorFunction(buff, iecmsBGR, DestScanline, iecmsBGR, outputWidth, IOParams);
end
else
begin
LinesRead := IEJPEG_Decomp_ReadScanlines(cinfo, @DestScanline, 1);
{$ifndef IEUSEDLLJPEGLIB}
if not Raw and (IEJPEG_Decomp_GetJpegColorSpace(cinfo) = IEJCS_RGB) then
_BGR2RGB(PRGB(DestScanline), Bitmap.Width);
{$endif}
end;
end;
end;
inc(result, LinesRead);
// OnProgress
with xProgress do
begin
inc(val, LinesRead);
if assigned(fOnProgress) and (trunc(per1 * val) <> lper) then
begin
lper := trunc(per1 * val);
fOnProgress(Sender, lper);
end;
end;
if xProgress.Aborting^ then
break;
end;
except
xProgress.Aborting^ := true;
exit;
end;
if result < MaxRows then
begin
xProgress.Aborting^ := true;
exit;
end;
finally
if freeICC and HasICC and (IOParams.InputICCProfile.IsTransforming) then
begin
// ICC profile applied: free CMS transform and remove jpeg ICC markers
IOParams.InputICCProfile.FreeTransform;
IOParams.JPEG_MarkerList.DeleteMarkerInstances(M_APP2);
if IEGlobalSettings().EXIFInteroperabilityIndexUsage = ieiiApplyAndReset then
begin
IOParams.EXIF_InteropIndex := '';
IOParams.EXIF_InteropVersion := '';
end;
end
else
if assigned(IOParams.fInputICC) then
begin
// ICC profile didn't apply
Bitmap.ColorProfile.Assign(IOParams.InputICCProfile);
end;
if (not xProgress.Aborting^) and not Preview then
begin
IEJPEG_DisableAbortings(jerr); // from this point decoder can't generate aborting
try
IEJPEG_Decomp_FinishDecompress(cinfo);
except
xProgress.Aborting^ := true;
end;
end;
IEJPEG_Decomp_DestroyDecompress(cinfo);
if buff <> nil then
freemem(buff);
if IEJPEG_GetNumWarnings(jerr) <> 0 then
begin
IOParams.JPEG_WarningTot := IEJPEG_GetNumWarnings(jerr);
IOParams.JPEG_WarningCode := IEJPEG_GetErrMsgCode(jerr);
end;
if adjustOrientation then
begin
IEAdjustEXIFOrientation(Bitmap, IOParams.EXIF_Orientation);
IOParams.EXIF_Orientation := 1;
end;
IEJPEG_Decomp_CleanupReadStream(cinfo);
IEJPEG_FreeErrorManager(jerr);
IEJPEG_Decomp_FreeDecompStruct(cinfo);
end;
end;
procedure WriteICCIntoJpegMarkers(IOParams: TIOParams);
const
magic: AnsiString = 'ICC_PROFILE'#0; // requires 12 bytes
MAXSEQLEN = 32000;
MARKERTYPE = M_APP2;
var
iccLen: integer;
iccData: pbyte;
seqCount: integer;
seq: integer;
seqLen: integer;
buf: pbytearray;
begin
IOParams.JPEG_MarkerList.DeleteMarkerInstances(MARKERTYPE);
iccLen := IOParams.InputICCProfile.RawLength;
iccData := IOParams.InputICCProfile.Raw;
seqCount := ceil(iccLen / MAXSEQLEN);
getmem(buf, MAXSEQLEN + 14);
CopyMemory(@buf[0], @magic[1], length(magic)); // length(magic) is 12, includes trailing #0
for seq := 1 to seqCount do
begin
buf[12] := seq;
buf[13] := seqCount;
seqLen := imin(MAXSEQLEN, iccLen);
CopyMemory(@buf[14], iccData, seqLen);
IOParams.JPEG_MarkerList.AddMarker(MARKERTYPE, @buf[0], seqLen + 14);
dec(iccLen, MAXSEQLEN);
inc(iccData, MAXSEQLEN);
end;
freemem(buf);
end;
// write jpeg stream
procedure WriteJpegStream(Stream: TStream; bitmap: TIEBitmap; var IOParams: TIOParams; var xProgress: TProgressRec);
var
cinfo: IEJPEG_Comp_Struct;
jerr: IEJPEG_ErrorManager;
SrcScanLine: pbyte;
xx: integer;
LinesPerCall, LinesWritten: DWORD;
FreeW: boolean;
WBitmap: TIEBitmap;
inColorSpace, outColorSpace: IEJ_COLOR_SPACE;
inputComponents: integer;
NullProgress: TProgressRec;
tlr: integer;
lper: integer;
imageWidth, imageHeight: dword;
rstate: boolean;
cmykBuffer: array of TCMYK;
rgbBuffer: array of TRGB;
begin
if (Bitmap.Width = 0) or (Bitmap.Height = 0) then
begin
xProgress.Aborting^ := true;
exit;
end;
lper := -1;
NullProgress := NullProgressRec( xProgress.Aborting, False );
cinfo := IEJPEG_Comp_AllocCompStruct();
jerr := IEJPEG_CreateErrorManager(xProgress.Aborting);
IEJPEG_Comp_SetErrorManager(cinfo, jerr);
IEJPEG_Comp_CreateCompress(cinfo);
IEJPEG_Comp_SetupWriteStream(cinfo, Stream, xProgress.Aborting);
// merge alpha channel
rstate := Bitmap.HasAlphaChannel;
if rstate then
begin
Bitmap.SaveState();
Bitmap.RemoveAlphaChannel(true);
end;
// adjust Bitmap pixel format
if Bitmap.PixelFormat <> ie24RGB then
begin
WBitmap := TIEBitmap.Create;
WBitmap.Assign(Bitmap);
WBitmap.PixelFormat := ie24RGB; // converts to 24bit
FreeW := true;
end
else
begin
WBitmap := Bitmap;
FreeW := false;
end;
try
if xProgress.Aborting^ then
exit;
imageWidth := WBitmap.Width;
imageHeight := WBitmap.Height;
IEJPEG_Comp_SetImageWidth(cinfo, imageWidth);
IEJPEG_Comp_SetImageHeight(cinfo, imageHeight);
inputComponents := 3;
inColorSpace := IEJCS_RGB;
outColorSpace := IEJCS_RGB;
case IOParams.JPEG_ColorSpace of
ioJPEG_GRAYLEV: outColorSpace := IEJCS_GRAYSCALE;
ioJPEG_YCbCr: outColorSpace := IEJCS_YCbCr;
ioJPEG_CMYK:
begin
inputComponents := 4;
inColorSpace := IEJCS_CMYK;
outColorSpace := IEJCS_CMYK;
end;
ioJPEG_YCbCrK:
begin
inputComponents := 4;
inColorSpace := IEJCS_CMYK;
outColorSpace := IEJCS_YCCK;
end;
end;
IEJPEG_Comp_SetInColorSpace(cinfo, inColorSpace);
IEJPEG_Comp_SetInputComponents(cinfo, inputComponents);
IEJPEG_Comp_SetDefaults(cinfo);
IEJPEG_Comp_SetColorSpace(cinfo, outColorSpace);
IEJPEG_Comp_SetDensityUnit(cinfo, 1);
if IOParams.dpix <> 0 then
IEJPEG_Comp_SetXDensity(cinfo, IOParams.dpix)
else
IEJPEG_Comp_SetXDensity(cinfo, IEGlobalSettings().DefaultDPIX);
if IOParams.dpiy <> 0 then
IEJPEG_Comp_SetYDensity(cinfo, IOParams.dpiy)
else
IEJPEG_Comp_SetYDensity(cinfo, IEGlobalSettings().DefaultDPIY);
case IOParams.JPEG_DCTMethod of
ioJPEG_ISLOW: IEJPEG_Comp_SetDCTMethod(cinfo, IEJDCT_ISLOW);
ioJPEG_IFAST: IEJPEG_Comp_SetDCTMethod(cinfo, IEJDCT_IFAST);
ioJPEG_FLOAT: IEJPEG_Comp_SetDCTMethod(cinfo, IEJDCT_FLOAT);
end;
IEJPEG_Comp_SetOptimizeCoding(cinfo, IOParams.JPEG_OptimalHuffman);
IEJPEG_Comp_SetSmoothingFactor(cinfo, IOParams.JPEG_Smooth);
IEJPEG_Comp_SetQuality(cinfo, IOParams.JPEG_Quality, true);
if IOParams.JPEG_ColorSpace = ioJPEG_YCbCr then
begin
if IOParams.JPEG_CromaSubsampling = ioJPEG_MEDIUM then
begin
// 4:2:2
IEJPEG_Comp_SetSampleFactor(cinfo, 0, 2, 1);
end
else
if IOParams.JPEG_CromaSubsampling = ioJPEG_HIGH then
begin
// 4:1:1
IEJPEG_Comp_SetSampleFactor(cinfo, 0, 2, 2);
end
else
if IOParams.JPEG_CromaSubsampling = ioJPEG_NONE then
begin
// 4:4:4
IEJPEG_Comp_SetSampleFactor(cinfo, 0, 1, 1);
end;
IEJPEG_Comp_SetSampleFactor(cinfo, 1, 1, 1);
IEJPEG_Comp_SetSampleFactor(cinfo, 2, 1, 1);
end;
if IOParams.JPEG_ColorSpace=ioJPEG_CMYK then
IEJPEG_Comp_SetWriteJFIFHeader(cinfo, true); // added in 2.2.1a to write JFIF (dpi info...) when you change color space (CMYK...)
if IOParams.JPEG_Progressive then
IEJPEG_Comp_SimpleProgression(cinfo);
IEJPEG_Comp_StartCompress(cinfo, true);
// write ICC profile into APP2 markers (and remove old ones, if necessary)
if assigned(IOParams.InputICCProfile) and IOParams.InputICCProfile.IsValid and not IOParams.InputICCProfile.IsApplied then
WriteICCIntoJpegMarkers(IOParams);
IOParams.JPEG_MarkerList.Sort;
with IOParams.JPEG_MarkerList do
for xx := 0 to Count - 1 do
if MarkerType[xx] <> JPEG_APP14 then // APP14 specifies the color format (not used by imageen)
IEJPEG_Comp_WriteMarker(cinfo, MarkerType[xx], pbyte(MarkerData[xx]), MarkerLength[xx]);
LinesPerCall := 1;
xProgress.per1 := 100 / imageHeight;
xProgress.val := 0;
if inColorSpace = IEJCS_CMYK then
// allocate buffer for RGB->CMYK conversion
SetLength(cmykBuffer, imageWidth * LinesPerCall);
if (outColorSpace = IEJCS_RGB) or (inColorSpace = IEJCS_RGB) then
// allocate buffer for BGR->RGB conversion
SetLength(rgbBuffer, imageWidth * LinesPerCall);
tlr := 0;
while IEJPEG_Comp_GetNextScanlineIndex(cinfo) < imageHeight do
begin
SrcScanline := WBitmap.Scanline[tlr];
if inColorSpace = IEJCS_CMYK then
begin
// converts RGB to CMYK (Params.JPEG_ColorSpace=ioJPEG_CMYK or params.JPEG_ColorSpace=ioJPEG_YCbCrK)
IEGlobalSettings().ConvertColorFunction(SrcScanline, iecmsBGR, cmykBuffer, iecmsCMYK, imageWidth, nil); // IOParams=nil because we cannot use profiles on saving
LinesWritten := IEJPEG_Comp_WriteScanlines(cinfo, @cmykBuffer, LinesPerCall);
end
else
if outColorSpace = IEJCS_RGB then
begin
{$ifdef IEUSEDLLJPEGLIB}
LinesWritten := IEJPEG_Comp_WriteScanlines(cinfo, @SrcScanline, LinesPerCall);
{$else}
// converts BGR to RGB (params.JPEG_ColorSpace=ioJPEG_RGB)
_CopyBGR_RGB(@rgbBuffer[0], PRGB(SrcScanline), imageWidth);
LinesWritten := IEJPEG_Comp_WriteScanlines(cinfo, @rgbBuffer, LinesPerCall);
{$endif}
end
{$ifdef IEUSEDLLJPEGLIB}
else
if (inColorSpace = IEJCS_RGB) and (outColorSpace <> IEJCS_GRAYSCALE) then
begin
// ioJPEG_RGB (Params.JPEG_ColorSpace=ioJPEG_YCbCr)
_CopyBGR_RGB(@rgbBuffer[0], PRGB(SrcScanline), imageWidth);
LinesWritten := IEJPEG_Comp_WriteScanlines(cinfo, @rgbBuffer, LinesPerCall);
end
{$endif}
else
begin
LinesWritten := IEJPEG_Comp_WriteScanlines(cinfo, @SrcScanline, LinesPerCall);
end;
inc(tlr, LinesWritten);
// OnProgress
with xProgress do
begin
inc(val, LinesWritten);
if assigned(fOnProgress) and (trunc(per1 * val)<>lper) then
begin
lper := trunc(per1 * val);
fOnProgress(Sender, lper);
end;
end;
if xProgress.Aborting^ then
break;
end;
finally
if not xProgress.Aborting^ then
IEJPEG_Comp_FinishCompress(cinfo);
IEJPEG_Comp_DestroyCompress(cinfo);
if FreeW then
FreeAndNil(WBitmap);
IEJPEG_Comp_CleanupWriteStream(cinfo);
IEJPEG_FreeErrorManager(jerr);
IEJPEG_Comp_FreeCompStruct(cinfo);
if rstate then
Bitmap.RestoreState();
end;
end;
(*
Transform can be:
JXFORM_NONE // 0=no transformation */
JXFORM_CUT // cut out part of the image
JXFORM_FLIP_H // 2=horizontal flip */
JXFORM_FLIP_V // 3=vertical flip */
JXFORM_TRANSPOSE // 4=transpose across UL-to-LR axis */
JXFORM_TRANSVERSE // 5=transpose across UR-to-LL axis */
JXFORM_ROT_90 // 6=90-degree clockwise rotation */
JXFORM_ROT_180 // 7=180-degree rotation */
JXFORM_ROT_270 // 8=270-degree clockwise (or 90 ccw) */
CopyMarkers can be:
JCOPYOPT_NONE // copy no optional markers */
JCOPYOPT_COMMENTS // copy only comment (COM) markers */
JCOPYOPT_ALL // copy all optional markers */
*)
// TIEJpegTransform = (jtNone, jtCut, jtHorizFlip, jtVertFlip, jtTranspose, jtTransverse, jtRotate90, jtRotate180, jtRotate270);
procedure IEJpegLosslessTransform(InStream, OutStream: TStream; var xProgress: TProgressRec; Transform: integer; GrayScale: boolean; CopyMarkers: integer; CutRect: TRect; updateEXIF: boolean);
var
srcinfo: IEJPEG_Decomp_Struct;
dstinfo: IEJPEG_Comp_Struct;
jerr: IEJPEG_ErrorManager;
spos: int64;
transformoption: IEJPEG_Transform;
src_coef_arrays, dst_coef_arrays: pointer;
copyoption: IEJCOPY_OPTION;
ie: TImageEnView;
ms: TMemoryStream;
begin
ms := nil;
copyoption := CopyMarkers;
spos := JpegTryStream(InStream, true);
if spos = -1 then
begin
xProgress.Aborting^ := true;
exit;
end;
InStream.Position := spos;
transformoption := nil;
jerr := nil;
srcinfo := nil;
dstinfo := nil;
try
srcinfo := IEJPEG_Decomp_AllocDecompStruct();
dstinfo := IEJPEG_Comp_AllocCompStruct();
jerr := IEJPEG_CreateErrorManager(xProgress.Aborting);
IEJPEG_Decomp_SetErrorManager(srcinfo, jerr);
IEJPEG_Comp_SetErrorManager(dstinfo, jerr);
IEJPEG_Decomp_CreateDecompress(srcinfo);
IEJPEG_Decomp_SetupReadStream(srcinfo, InStream, xProgress.Aborting);
IEJPEG_Comp_CreateCompress(dstinfo);
transformoption := IEJPEG_CreateTransformOption(Transform, true, GrayScale, CutRect.Left, CutRect.Top, CutRect.Right - CutRect.Left + 1, CutRect.Bottom - CutRect.Top + 1);
if updateEXIF then
begin
ms := TMemoryStream.Create;
IEJPEG_Comp_SetupWriteStream(dstinfo, ms, xProgress.Aborting);
end
else
IEJPEG_Comp_SetupWriteStream(dstinfo, OutStream, xProgress.Aborting);
if xProgress.Aborting^ then
exit;
try
IEJPEG_JCopyMarkersSetup(srcinfo, copyoption);
IEJPEG_Decomp_ReadHeader(srcinfo, true);
IEJPEG_JTransformRequestWorkspace(srcinfo, transformoption);
src_coef_arrays := IEJPEG_ReadCoefficients(srcinfo);
IEJPEG_CopyCritialParameters(srcinfo, dstinfo);
dst_coef_arrays := IEJPEG_JTransformAdjustParameters(srcinfo, dstinfo, src_coef_arrays, transformoption);
IEJPEG_WriteCoefficients(dstinfo, dst_coef_arrays);
IEJPEG_JCopyMarkersExecute(srcinfo, dstinfo, copyoption);
IEJPEG_JTransformExecuteTransformation(srcinfo, dstinfo, src_coef_arrays, transformoption);
IEJPEG_Comp_FinishCompress(dstinfo);
IEJPEG_Comp_DestroyCompress(dstinfo);
IEJPEG_Decomp_FinishDecompress(srcinfo);
IEJPEG_Decomp_DestroyDecompress(srcinfo);
except
xProgress.Aborting^ := true;
exit;
end;
if xProgress.Aborting^ then
exit;
finally
IEJPEG_FreeTransformOption(transformoption);
IEJPEG_Decomp_CleanupReadStream(srcinfo);
IEJPEG_Comp_CleanupWriteStream(dstinfo);
IEJPEG_FreeErrorManager(jerr);
IEJPEG_Decomp_FreeDecompStruct(srcinfo);
IEJPEG_Comp_FreeCompStruct(dstinfo);
if updateEXIF then
begin
// update EXIF rotation tag to 1 (unrotated) and the thumbnail
ie := TImageEnView.Create(nil);
try
inStream.Position := spos;
ie.IO.Params.JPEG_Scale := ioJPEG_EIGHTH;
ms.Position := 0;
ie.IO.LoadFromStream(ms);
if Transform <> IEJXFORM_CUT then // 3.0.3
ie.IO.Params.EXIF_Orientation := 1;
ie.IO.Params.UpdateEXIFThumbnail();
ms.Position := 0;
ie.IO.InjectJpegEXIFStream(ms, OutStream);
finally
ie.Free;
end;
end;
if ms<>nil then
ms.Free;
end;
end;
// return >=0 if Stream contains a jpeg block (>0 is the starting position of the JFIF block)
// return -1 if the stream doesn't contains the jpeg in the first 100 bytes
// save stream position
function JpegTryStream(Stream: TStream; find: boolean): int64;
var
sp: int64;
SOImarker: word; // D8FF
bb: byte;
begin
sp := Stream.Position;
result := sp;
repeat
Stream.Read(SOImarker, 2);
Stream.Read(bb, 1);
if (SOImarker = $D8FF) and (bb = $FF) then
break;
inc(result);
Stream.Position := result;
until (result = sp + 150) or not find;
if not find then
begin
if result=sp then
result := 0 // found
else
result := -1; // not found
end
else
begin
if result = sp + 150 then
result := -1;
end;
Stream.Position := sp;
end;
function IEJpegInjectIPTC(InputStream, OutputStream: TStream; iptc: TIEIPTCInfoList; var xProgress: TProgressRec): boolean;
var
b1, b2, b3: byte;
w: word;
abort: boolean;
app13done: boolean;
l: integer;
per, lper: integer;
//
procedure WriteAPP13;
var
b: byte;
buf: pointer;
blen: integer;
begin
if not app13done then
begin
iptc.SaveToStandardBuffer(buf, blen, true);
if blen > 0 then
begin
b := $FF;
OutputStream.Write(b, 1);
b := M_APP13;
OutputStream.Write(b, 1);
w := IESwapWord(blen + 2);
OutputStream.Write(w, 2);
OutputStream.Write(pbyte(buf)^, blen);
end;
if buf <> nil then
freemem(buf);
app13done := true;
end;
end;
procedure WriteTag;
begin
OutputStream.Write(b1, 1);
OutputStream.Write(b2, 1);
InputStream.Read(w, 2);
OutputStream.Write(w, 2);
w := IESwapWord(w);
if w-2 > 0 then
IECopyFrom(OutputStream, InputStream, w - 2);
end;
procedure ByPass;
begin
InputStream.Read(w, 2);
w := IESwapWord(w);
InputStream.Position := InputStream.Position + w - 2;
end;
function PeekNext: byte;
var
b1, b2: byte;
begin
InputStream.Read(b1, 1);
InputStream.Read(b2, 1);
if b1=$FF then
result := b2
else
result := 0;
InputStream.Seek(-2, soCurrent);
end;
begin
lper := -1;
xProgress.per1 := 100 / (InputStream.Size - InputStream.Position);
abort := false;
app13done := false;
while (not abort) and (InputStream.Read(b1, 1) > 0) do
begin
if b1 = $FF then
begin
InputStream.Read(b2, 1);
case b2 of
M_APP0,
M_APP1,
M_APP2,
M_APP3,
M_APP4,
M_APP5,
M_APP6,
M_APP7,
M_APP8,
M_APP9,
M_APP10,
M_APP11,
M_APP12:
begin
WriteTag;
b3 := PeekNext;
if (b3<M_APP0) or (b3>M_APP13) then
WriteAPP13;
end;
M_APP13:
begin
// replace APP13
ByPass;
WriteAPP13; // does app13done=true
end;
M_SOF0,
M_SOF1,
M_SOF2,
M_SOF3,
M_SOF5,
M_SOF6,
M_SOF7,
M_SOF9,
M_SOF10,
M_SOF11,
M_SOF13,
M_SOF14,
M_SOF15,
M_APP14,
M_APP15,
M_JPG,
M_DHT,
M_DAC,
M_SOS,
M_DQT,
M_DNL,
M_DRI,
M_DHP,
M_EXP,
M_JPG0, M_JPG13,
M_JPG1, M_JPG2, M_JPG3, M_JPG4, M_JPG5, M_JPG6, M_JPG7, M_JPG8, M_JPG9, M_JPG10, M_JPG11, M_JPG12,
M_COM:
begin
if b2=M_DQT then
WriteAPP13;
WriteTag;
end;
M_RST0,
M_RST1,
M_RST2,
M_RST3,
M_RST4,
M_RST5,
M_RST6,
M_RST7,
M_BYPASS,
M_SOI,
M_TEM:
begin
OutputStream.Write(b1, 1);
OutputStream.Write(b2, 1);
end;
M_EOI:
begin
OutputStream.Write(b1, 1);
OutputStream.Write(b2, 1);
// copy rest of the file unparsed
l := InputStream.Size - InputStream.Position;
if l > 0 then
IECopyFrom(OutputStream, InputStream, l);
end;
else
begin
abort := true;
end;
end;
// OnProgress (put here, because out of this could be very slow)
with xProgress do
begin
per := trunc(per1 * InputStream.Position);
if (per <> lper) and assigned(fOnProgress) then
fOnProgress(Sender, per);
lper := per;
end;
end
else
begin
OutputStream.Write(b1, 1);
end;
end;
result := (not abort) and app13done;
end;
function IEJpegInjectEXIF(XInputStream, XOutputStream: TStream; exif: TIOParams; var xProgress: TProgressRec): boolean;
var
per, lper: integer;
b1, b2: byte;
w: word;
abort: boolean;
app1done: boolean;
l: int64;
InputStream: TIEBufferedReadStream;
OutputStream: TIEBufferedWriteStream;
procedure WriteAPP1();
var
b: byte;
buf: pointer;
blen: integer;
begin
if not app1done and exif.EXIF_HasEXIFData then
begin
SaveEXIFToStandardBuffer(exif, buf, blen, true);
if blen > 0 then
begin
b := $FF;
OutputStream.Write(b, 1);
b := M_APP1;
OutputStream.Write(b, 1);
w := IESwapWord(blen + 2);
OutputStream.Write(w, 2);
OutputStream.Write(pbyte(buf)^, blen);
end;
if buf <> nil then
freemem(buf);
end;
app1done := true;
end;
procedure WriteTag();
begin
OutputStream.Write(b1, 1);
OutputStream.Write(b2, 1);
InputStream.Read(w, 2);
OutputStream.Write(w, 2);
w := IESwapWord(w);
if w - 2 > 0 then
IECopyFrom(OutputStream, InputStream, w - 2);
end;
procedure ByPass();
begin
InputStream.Read(w, 2);
w := IESwapWord(w);
InputStream.position := InputStream.position + w - 2;
end;
function IsEXIFTag(): boolean;
var
s: array [0..4] of AnsiChar;
begin
InputStream.Read(w, 2);
InputStream.Read(s[0], 4);
s[4] := #0;
result := (s = 'Exif');
InputStream.Seek(- 2 - 4, soCurrent);
end;
begin
InputStream := TIEBufferedReadStream.Create(XInputStream, 65536);
OutputStream := TIEBufferedWriteStream.Create(XOutputStream, 65536);
try
lper := -1;
xProgress.per1 := 100 / (InputStream.Size - InputStream.Position);
abort := false;
app1done := false;
while (not abort) and (InputStream.Read(b1, 1) > 0) do
begin
if b1 = $FF then
begin
InputStream.Read(b2, 1);
case b2 of
M_APP0:
begin
WriteTag();
WriteAPP1();
end;
M_APP1:
begin
if IsEXIFTag() then
ByPass() // this is exif, bypass
else
WriteTag(); // this is not exif, write back
WriteAPP1(); // if not already done
end;
M_SOF0, M_SOF1, M_SOF2, M_SOF3, M_SOF5, M_SOF6, M_SOF7, M_SOF9, M_SOF10, M_SOF11, M_SOF13, M_SOF14, M_SOF15,
M_JPG,
M_DHT,
M_DAC,
M_DQT,
M_DNL,
M_DRI,
M_DHP,
M_EXP,
M_APP2, M_APP3, M_APP4, M_APP5, M_APP6, M_APP7, M_APP8, M_APP9, M_APP10, M_APP11, M_APP12, M_APP13, M_APP14, M_APP15,
M_JPG0, M_JPG1, M_JPG2, M_JPG3, M_JPG4, M_JPG5, M_JPG6, M_JPG7, M_JPG8, M_JPG9, M_JPG10, M_JPG11, M_JPG12, M_JPG13,
M_COM,
M_SOS:
begin
WriteTag();
end;
M_RST0, M_RST1, M_RST2, M_RST3, M_RST4, M_RST5, M_RST6, M_RST7,
M_BYPASS,
M_SOI,
M_TEM:
begin
OutputStream.Write(b1, 1);
OutputStream.Write(b2, 1);
end;
M_EOI:
begin
OutputStream.Write(b1, 1);
OutputStream.Write(b2, 1);
// copy rest of the file unparsed
l := InputStream.Size - InputStream.Position;
if l > 0 then
IECopyFrom(OutputStream, InputStream, l);
end;
else
begin
abort := true;
end;
end;
// OnProgress (put here, because out of this could be very slow)
with xProgress do
begin
per := trunc(per1 * InputStream.Position);
if (per <> lper) and assigned(fOnProgress) then
fOnProgress(Sender, per);
lper := per;
end;
end
else
begin
OutputStream.Write(b1, 1);
end;
end;
finally
InputStream.Free;
OutputStream.Free;
end;
result := (not abort) and app1done;
end;
// returns number of qtables
function IEGetJpegQuality(InputStream: TStream; var QTables: pointer): integer;
const
std_luminance_quant_tbl: array[0..63] of integer = (
16, 11, 12, 14, 12, 10, 16, 14,
13, 14, 18, 17, 16, 19, 24, 40,
26, 24, 22, 22, 24, 49, 35, 37,
29, 40, 58, 51, 61, 60, 57, 51,
56, 55, 64, 72, 92, 78, 64, 68,
87, 69, 55, 56, 80, 109, 81, 87,
95, 98, 103, 104, 103, 62, 77, 113,
121, 112, 100, 120, 92, 101, 103, 99
);
const
std_chrominance_quant_tbl: array[0..63] of integer = (
17, 18, 18, 24, 21, 24, 47, 26,
26, 47, 99, 66, 56, 66, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
);
const
deftabs: array[0..1] of pointer =
(@std_luminance_quant_tbl, @std_chrominance_quant_tbl);
const
MAXQTABLES = 15;
var
b1, b2, bx: byte;
tempb: byte;
w: word;
tableindex: integer;
table: pintegerarray;
cumsf: double;
cumsf2: double;
allones: boolean;
row, col: integer;
val: word;
x: double;
qual: double;
qt: pintegerarray;
//
procedure ByPass;
begin
InputStream.Read(w, 2);
w := IESwapWord(w);
InputStream.position := InputStream.position + w - 2;
end;
//
begin
getmem(qt, MAXQTABLES * sizeof(integer));
QTables := qt;
result := 0;
while (InputStream.Read(b1, 1) > 0) do
begin
if b1 = $FF then
begin
InputStream.Read(b2, 1);
case b2 of
M_DQT:
begin
InputStream.Read(w, 2);
w := IESwapWord(w);
dec(w, 2);
while (w > 0) do
begin
table := nil;
cumsf := 0;
cumsf2 := 0;
allones := true;
InputStream.Read(bx, 1);
dec(w);
tableindex := bx and $0F;
if (tableindex < 2) then
table := deftabs[tableindex];
for row := 0 to 7 do
begin
for col := 0 to 7 do
begin
if (bx shr 4) <> 0 then
begin
InputStream.Read(val, 2);
val := IESwapWord(val);
dec(w, 2);
end
else
begin
InputStream.Read(tempb, 1);
val := tempb;
dec(w);
end;
if (table <> nil) then
begin
x := 100.0 * val / table[row * 8 + col];
cumsf := cumsf + x;
cumsf2 := cumsf2 + (x * x);
if (val <> 1) then
allones := false;
end;
end;
end;
if (table <> nil) then
begin
cumsf := cumsf / 64.0;
//cumsf2 := cumsf2 / 64.0;
if (allones) then
qual := 100.0
else
if (cumsf <= 100.0) then
qual := (200.0 - cumsf) / 2.0
else
qual := 5000.0 / cumsf;
qt[result] := round(qual);
inc(result);
if result = MAXQTABLES then
exit;
end;
end;
exit;
end;
M_APP1,
M_SOF0,
M_SOF1,
M_SOF2,
M_SOF3,
M_SOF5,
M_SOF6,
M_SOF7,
M_SOF9,
M_SOF10,
M_SOF11,
M_SOF13,
M_SOF14,
M_SOF15,
M_JPG,
M_DHT,
M_DAC,
M_SOS,
//M_DQT,
M_DNL,
M_DRI,
M_DHP,
M_EXP,
M_APP0,
M_APP2,
M_APP3,
M_APP4,
M_APP5,
M_APP6,
M_APP7,
M_APP8,
M_APP9,
M_APP10,
M_APP11,
M_APP12,
M_APP13,
M_APP14,
M_APP15,
M_JPG0, M_JPG13,
M_JPG1, M_JPG2, M_JPG3, M_JPG4, M_JPG5, M_JPG6, M_JPG7, M_JPG8, M_JPG9, M_JPG10, M_JPG11, M_JPG12,
M_COM:
begin
ByPass;
end;
M_EOI:
begin
break;
end;
end;
end
else
begin
end;
end;
end;
// find the position where the jpeg ends, and return the jpeg length
// saves the previous stream position
function IEGetJpegLength(InputStream: TStream): integer;
var
b1, b2: byte;
w: word;
lpos: int64;
ms: TIEBufferedReadStream;
//
procedure ByPass;
begin
ms.Read(w, 2);
w := IESwapWord(w);
ms.position := ms.position + w - 2;
end;
//
begin
lpos := InputStream.Position;
ms := TIEBufferedReadStream.Create(InputStream, 65536, false);
try
while (ms.Read(b1, 1) > 0) do
begin
if b1 = $FF then
begin
ms.Read(b2, 1);
case b2 of
M_APP1, M_SOF0, M_SOF1, M_SOF2, M_SOF3, M_SOF5, M_SOF6, M_SOF7, M_SOF9, M_SOF10, M_SOF11, M_SOF13,
M_SOF14, M_SOF15, M_JPG, M_DHT, M_DAC, M_SOS, M_DQT, M_DNL, M_DRI, M_DHP, M_EXP, M_APP0, M_APP2,
M_APP3, M_APP4, M_APP5, M_APP6, M_APP7, M_APP8, M_APP9, M_APP10, M_APP11, M_APP12, M_APP13, M_APP14,
M_APP15, M_JPG0, M_JPG13, M_JPG1, M_JPG2, M_JPG3, M_JPG4, M_JPG5, M_JPG6, M_JPG7, M_JPG8, M_JPG9,
M_JPG10, M_JPG11, M_JPG12, M_COM:
ByPass;
M_EOI:
break;
end;
end;
end;
result := ms.Position - lpos;
finally
ms.Free();
InputStream.Position := lpos;
end;
end;
// restores input stream position
// stores thc ICC in Params.InputICCProfile
procedure IEGetJpegICC(InputStream: TStream; Params: TIOParams);
var
b1, b2: byte;
w: word;
lpos: int64;
icc: TList;
icc_len: TList;
i: integer;
buf: pbytearray;
seq: byte;
cnt, rcnt: byte;
buf2, buf3, buf4: pbyte;
osize: integer;
ms: TIEBufferedReadStream;
//
procedure ByPass;
begin
ms.Read(w, 2);
w := IESwapWord(w);
ms.position := ms.position + w - 2;
end;
//
begin
lpos := InputStream.Position;
ms := TIEBufferedReadStream.Create(InputStream, 65536, false);
icc := TList.Create;
icc_len := TList.Create;
osize := 0;
rcnt := 0;
try
while (ms.Read(b1, 1) > 0) do
begin
if b1 = $FF then
begin
ms.Read(b2, 1);
case b2 of
M_APP2:
begin
ms.Read(w, 2);
w := IESwapWord(w);
getmem(buf, w);
ms.Read(buf[0], w - 2);
if PAnsiChar(buf) <> 'ICC_PROFILE' then
freemem(buf)
else
begin
seq := buf[12] - 1; // sequence number
cnt := buf[13]; // sequence count
if icc.Count <= seq then
begin
icc.Count := seq + 1;
icc_len.Count := seq + 1;
end;
icc[seq] := buf;
icc_len[seq] := pointer(w - 16);
inc(osize, w - 16);
inc(rcnt);
if rcnt = cnt then
break;
end;
end;
M_APP1,
M_JPG,
M_DHT,
M_DAC,
M_SOS,
M_DQT,
M_DNL,
M_DRI,
M_DHP,
M_EXP,
M_APP0,
M_APP3,
M_APP4,
M_APP5,
M_APP6,
M_APP7,
M_APP8,
M_APP9,
M_APP10,
M_APP11,
M_APP12,
M_APP13,
M_APP14,
M_APP15,
M_JPG0, M_JPG13,
M_JPG1, M_JPG2, M_JPG3, M_JPG4, M_JPG5, M_JPG6, M_JPG7, M_JPG8, M_JPG9, M_JPG10, M_JPG11, M_JPG12,
M_COM:
begin
ByPass;
end;
M_SOF0,
M_SOF1,
M_SOF2,
M_SOF3,
M_SOF5,
M_SOF6,
M_SOF7,
M_SOF9,
M_SOF10,
M_SOF11,
M_SOF13,
M_SOF14,
M_SOF15:
break; // Read up to SOF markers, otherwise is very slow to parse in large images)
M_EOI:
begin
break;
end;
end;
end;
end;
finally
if osize > 0 then
begin
getmem(buf2, osize);
buf3 := buf2;
for i := 0 to icc.Count - 1 do
begin
buf4 := icc[i];
inc(buf4, 14);
CopyMemory(buf3, buf4, integer(icc_len[i]));
inc(buf3, integer(icc_len[i]));
freemem(icc[i]);
end;
Params.InputICCProfile.LoadFromBuffer(buf2, osize);
freemem(buf2);
end;
FreeAndNil(icc);
FreeAndNil(icc_len);
FreeAndNil(ms);
InputStream.Position := lpos;
end;
end;
procedure IEGetJPEGSize(const aFileName: string; var aWidth, aHeight: integer);
var
lAborting: boolean;
lJPEG_error: IEJPEG_ErrorManager;
lJPEG_struct: IEJPEG_Decomp_Struct;
lPos: int64;
lProgressRec: TProgressRec;
lStream: TIEWideFileStream;
begin
lProgressRec := NullProgressRec( lAborting );
lStream := TIEWideFileStream.Create(aFileName, fmOpenRead or fmShareDenyWrite);
try
// Find JPEG stream start
lPos := JpegTryStream(lStream, true);
if lPos = -1 then
begin
aHeight := -1;
aWidth := -1;
exit;
end;
lStream.Position := lPos;
lJPEG_error := IEJPEG_CreateErrorManager(lProgressRec.Aborting);
lJPEG_struct := IEJPEG_Decomp_AllocDecompStruct();
IEJPEG_Decomp_SetErrorManager(lJPEG_struct, lJPEG_error);
try
// Allocate and initialize a JPEG decompression object
IEJPEG_Decomp_CreateDecompress(lJPEG_struct);
IEJPEG_Decomp_SetupReadStream(lJPEG_struct, lStream, lProgressRec.Aborting);
IEJPEG_Decomp_ReadHeader(lJPEG_struct, false);
aWidth := IEJPEG_Decomp_GetImageWidth(lJPEG_struct);
aHeight := IEJPEG_Decomp_GetImageHeight(lJPEG_struct);
finally
IEJPEG_Decomp_DestroyDecompress(lJPEG_struct);
IEJPEG_Decomp_CleanupReadStream(lJPEG_struct);
IEJPEG_Decomp_FreeDecompStruct(lJPEG_struct);
IEJPEG_FreeErrorManager(lJPEG_error);
end;
finally
lStream.Free;
end;
end;
end.