home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Plus! (NZ) 2001 June
/
HDC50.iso
/
Info
/
Extras
/
Jpeg
/
JPEG.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1999-08-11
|
55KB
|
1,579 lines
{*******************************************************}
{ }
{ Borland Delphi Runtime Library }
{ JPEG Image Compression/Decompression Unit }
{ }
{ Copyright (c) 1997,99 Inprise Corporation }
{ }
{*******************************************************}
{$HPPEMIT '#pragma link "jpeg.obj"'}
unit jpeg;
interface
uses Windows, SysUtils, Classes, Graphics;
type
TJPEGData = class(TSharedImage)
private
FData: TCustomMemoryStream;
FHeight: Integer;
FWidth: Integer;
FGrayscale: Boolean;
protected
procedure FreeHandle; override;
public
destructor Destroy; override;
end;
TJPEGQualityRange = 1..100; // 100 = best quality, 25 = pretty awful
TJPEGPerformance = (jpBestQuality, jpBestSpeed);
TJPEGScale = (jsFullSize, jsHalf, jsQuarter, jsEighth);
TJPEGPixelFormat = (jf24Bit, jf8Bit);
TJPEGImage = class(TGraphic)
private
FImage: TJPEGData;
FBitmap: TBitmap;
FScaledWidth: Integer;
FScaledHeight: Integer;
FTempPal: HPalette;
FSmoothing: Boolean;
FGrayScale: Boolean;
FPixelFormat: TJPEGPixelFormat;
FQuality: TJPEGQualityRange;
FProgressiveDisplay: Boolean;
FProgressiveEncoding: Boolean;
FPerformance: TJPEGPerformance;
FScale: TJPEGScale;
FNeedRecalc: Boolean;
procedure CalcOutputDimensions;
function GetBitmap: TBitmap;
function GetGrayscale: Boolean;
procedure SetGrayscale(Value: Boolean);
procedure SetPerformance(Value: TJPEGPerformance);
procedure SetPixelFormat(Value: TJPEGPixelFormat);
procedure SetScale(Value: TJPEGScale);
procedure SetSmoothing(Value: Boolean);
protected
procedure AssignTo(Dest: TPersistent); override;
procedure Changed(Sender: TObject); override;
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
function Equals(Graphic: TGraphic): Boolean; override;
procedure FreeBitmap;
function GetEmpty: Boolean; override;
function GetHeight: Integer; override;
function GetPalette: HPALETTE; override;
function GetWidth: Integer; override;
procedure NewBitmap;
procedure NewImage;
procedure ReadData(Stream: TStream); override;
procedure ReadStream(Size: Longint; Stream: TStream);
procedure SetHeight(Value: Integer); override;
procedure SetPalette(Value: HPalette); override;
procedure SetWidth(Value: Integer); override;
procedure WriteData(Stream: TStream); override;
property Bitmap: TBitmap read GetBitmap; // volatile
public
constructor Create; override;
destructor Destroy; override;
procedure Compress;
procedure DIBNeeded;
procedure JPEGNeeded;
procedure Assign(Source: TPersistent); override;
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE); override;
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE); override;
// Options affecting / reflecting compression and decompression behavior
property Grayscale: Boolean read GetGrayscale write SetGrayscale;
property ProgressiveEncoding: Boolean read FProgressiveEncoding write FProgressiveEncoding;
// Compression options
property CompressionQuality: TJPEGQualityRange read FQuality write FQuality;
// Decompression options
property PixelFormat: TJPEGPixelFormat read FPixelFormat write SetPixelFormat;
property ProgressiveDisplay: Boolean read FProgressiveDisplay write FProgressiveDisplay;
property Performance: TJPEGPerformance read FPerformance write SetPerformance;
property Scale: TJPEGScale read FScale write SetScale;
property Smoothing: Boolean read FSmoothing write SetSmoothing;
end;
TJPEGDefaults = record
CompressionQuality: TJPEGQualityRange;
Grayscale: Boolean;
Performance: TJPEGPerformance;
PixelFormat: TJPEGPixelFormat;
ProgressiveDisplay: Boolean;
ProgressiveEncoding: Boolean;
Scale: TJPEGScale;
Smoothing: Boolean;
end;
var // Default settings for all new TJPEGImage instances
JPEGDefaults: TJPEGDefaults = (
CompressionQuality: 90;
Grayscale: False;
Performance: jpBestQuality;
PixelFormat: jf24Bit; // initialized to match video mode
ProgressiveDisplay: False;
ProgressiveEncoding: False;
Scale: jsFullSize;
Smoothing: True;
);
implementation
uses jconsts;
{ The following types and external function declarations are used to
call into functions of the Independent JPEG Group's (IJG) implementation
of the JPEG image compression/decompression public standard. The IJG
library's C source code is compiled into OBJ files and linked into
the Delphi application. Only types and functions needed by this unit
are declared; all IJG internal structures are stubbed out with
generic pointers to reduce internal source code congestion.
IJG source code copyright (C) 1991-1996, Thomas G. Lane. }
{$Z4} // Minimum enum size = dword
const
JPEG_LIB_VERSION = 61; { Version 6a }
JPEG_RST0 = $D0; { RST0 marker code }
JPEG_EOI = $D9; { EOI marker code }
JPEG_APP0 = $E0; { APP0 marker code }
JPEG_COM = $FE; { COM 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 }
{ Known color spaces. }
type
J_COLOR_SPACE = (
JCS_UNKNOWN, { error/unspecified }
JCS_GRAYSCALE, { monochrome }
JCS_RGB, { red/green/blue }
JCS_YCbCr, { Y/Cb/Cr (also known as YUV) }
JCS_CMYK, { C/M/Y/K }
JCS_YCCK { Y/Cb/Cr/K }
);
{ DCT/IDCT algorithm options. }
type
J_DCT_METHOD = (
JDCT_ISLOW, { slow but accurate integer algorithm }
JDCT_IFAST, { faster, less accurate integer method }
JDCT_FLOAT { floating-point: accurate, fast on fast HW (Pentium)}
);
{ Dithering options for decompression. }
type
J_DITHER_MODE = (
JDITHER_NONE, { no dithering }
JDITHER_ORDERED, { simple ordered dither }
JDITHER_FS { Floyd-Steinberg error diffusion dither }
);
{ 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
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: PChar);
{ 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 }
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 manger 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 }
jpeg_common_struct = packed 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 }
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 Delphi info
instance: TJPEGImage; // ptr to current TJPEGImage object
last_pass: Integer;
last_pct: Integer;
last_time: Integer;
last_scanline: Integer;
end;
{ Master record for a compression instance }
jpeg_compress_struct = packed record
common: jpeg_common_struct;
dest : jpeg_destination_mgr_ptr; { Destination for compressed data }
{ 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 }
image_height : JDIMENSION; { input image height }
input_components : Integer; { # of color components in input image }
in_color_space : J_COLOR_SPACE; { colorspace of input image }
input_gamma : double; { image gamma of input image }
// Compression parameters
data_precision : Integer; { bits of precision in image data }
num_components : Integer; { # of color components in JPEG image }
jpeg_color_space : J_COLOR_SPACE; { colorspace of JPEG image }
comp_info : Pointer;
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;
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 }
num_scans : Integer; { # of entries in scan_info array }
scan_info : Pointer; { script for multi-scan file, or NIL }
raw_data_in : LongBool; { TRUE=caller supplies downsampled data }
arith_code : LongBool; { TRUE=arithmetic coding, FALSE=Huffman }
optimize_coding : LongBool; { TRUE=optimize entropy encoding parms }
CCIR601_sampling : LongBool; { TRUE=first samples are cosited }
smoothing_factor : Integer; { 1..100, or 0 for no input smoothing }
dct_method : J_DCT_METHOD; { DCT algorithm selector }
restart_interval : UINT; { MCUs per restart, or 0 for no restart }
restart_in_rows : Integer; { if > 0, MCU rows per restart interval }
{ Parameters controlling emission of special markers. }
write_JFIF_header : LongBool; { should a JFIF marker be written? }
{ 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 }
X_density : UINT16; { Horizontal pixel density }
Y_density : UINT16; { Vertical pixel density }
write_Adobe_marker : LongBool; { should an Adobe marker be written? }
{ 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 }
{ 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 }
max_h_samp_factor : Integer; { largest h_samp_factor }
max_v_samp_factor : Integer; { largest v_samp_factor }
total_iMCU_rows : JDIMENSION; { # of iMCU rows to be input to coef ctlr }
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 : Integer; { # of DCT blocks per MCU }
MCU_membership : Array[0..C_MAX_BLOCKS_IN_MCU-1] of Integer;
Ss, Se, Ah, Al : Integer; { progressive JPEG parameters for scan }
{ Links to compression subobjects (methods and private variables of modules) }
master : Pointer;
main : Pointer;
prep : Pointer;
coef : Pointer;
marker : Pointer;
cconvert : Pointer;
downsample : Pointer;
fdct : Pointer;
entropy : Pointer;
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 : J_COLOR_SPACE; { colorspace of JPEG image }
{ Decompression processing parameters }
out_color_space : J_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 : J_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 : J_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 }
{ Data copied from JFIF marker: }
density_unit : UINT8; { JFIF code for pixel size units }
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 }
CCIR601_sampling : LongBool; { TRUE=first samples are cosited }
{ 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;
TJPEGContext = record
err: jpeg_error_mgr;
progress: jpeg_progress_mgr;
FinalDCT: J_DCT_METHOD;
FinalTwoPassQuant: Boolean;
FinalDitherMode: J_DITHER_MODE;
case byte of
0: (common: jpeg_common_struct);
1: (d: jpeg_decompress_struct);
2: (c: jpeg_compress_struct);
end;
{ 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
GetMem(Result, size);
end;
procedure _free(P: Pointer); cdecl;
begin
FreeMem(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 _fread(var buf; recsize, reccount: Integer; S: TStream): Integer; cdecl;
begin
Result := S.Read(buf, recsize * reccount);
end;
function _fwrite(const buf; recsize, reccount: Integer; S: TStream): Integer; cdecl;
begin
Result := S.Write(buf, recsize * reccount);
end;
function _fflush(S: TStream): Integer; cdecl;
begin
Result := 0;
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 := Integer(Trunc(f));
end;
var
__turboFloat: LongBool = False;
{$L jdapimin.obj}
{$L jmemmgr.obj}
{$L jmemnobs.obj}
{$L jdinput.obj}
{$L jdatasrc.obj}
{$L jdapistd.obj}
{$L jdmaster.obj}
{$L jdphuff.obj}
{$L jdhuff.obj}
{$L jdmerge.obj}
{$L jdcolor.obj}
{$L jquant1.obj}
{$L jquant2.obj}
{$L jdmainct.obj}
{$L jdcoefct.obj}
{$L jdpostct.obj}
{$L jddctmgr.obj}
{$L jdsample.obj}
{$L jidctflt.obj}
{$L jidctfst.obj}
{$L jidctint.obj}
{$L jidctred.obj}
{$L jdmarker.obj}
{$L jutils.obj}
{$L jcomapi.obj}
procedure jpeg_CreateDecompress (var cinfo : jpeg_decompress_struct;
version : integer; structsize : integer); external;
procedure jpeg_stdio_src(var cinfo: jpeg_decompress_struct;
input_file: TStream); 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;
{$L jdatadst.obj}
{$L jcparam.obj}
{$L jcapistd.obj}
{$L jcapimin.obj}
{$L jcinit.obj}
{$L jcmarker.obj}
{$L jcmaster.obj}
{$L jcmainct.obj}
{$L jcprepct.obj}
{$L jccoefct.obj}
{$L jccolor.obj}
{$L jcsample.obj}
{$L jcdctmgr.obj}
{$L jcphuff.obj}
{$L jfdctint.obj}
{$L jfdctfst.obj}
{$L jfdctflt.obj}
{$L jchuff.obj}
procedure jpeg_CreateCompress (var cinfo : jpeg_compress_struct;
version : integer; structsize : integer); external;
procedure jpeg_stdio_dest(var cinfo: jpeg_compress_struct;
output_file: TStream); 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: J_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;
type
EJPEG = class(EInvalidGraphic);
procedure InvalidOperation(const Msg: string); near;
begin
raise EInvalidGraphicOperation.Create(Msg);
end;
procedure JpegError(cinfo: j_common_ptr);
begin
raise EJPEG.CreateFmt(sJPEGError,[cinfo^.err^.msg_code]);
end;
procedure EmitMessage(cinfo: j_common_ptr; msg_level: Integer);
begin
//!!
end;
procedure OutputMessage(cinfo: j_common_ptr);
begin
//!!
end;
procedure FormatMessage(cinfo: j_common_ptr; buffer: PChar);
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);
{ TJPEGData }
destructor TJPEGData.Destroy;
begin
FData.Free;
inherited Destroy;
end;
procedure TJPEGData.FreeHandle;
begin
end;
{ TJPEGImage }
constructor TJPEGImage.Create;
begin
inherited Create;
NewImage;
FQuality := JPEGDefaults.CompressionQuality;
FGrayscale := JPEGDefaults.Grayscale;
FPerformance := JPEGDefaults.Performance;
FPixelFormat := JPEGDefaults.PixelFormat;
FProgressiveDisplay := JPEGDefaults.ProgressiveDisplay;
FProgressiveEncoding := JPEGDefaults.ProgressiveEncoding;
FScale := JPEGDefaults.Scale;
FSmoothing := JPEGDefaults.Smoothing;
end;
destructor TJPEGImage.Destroy;
begin
if FTempPal <> 0 then DeleteObject(FTempPal);
FBitmap.Free;
FImage.Release;
inherited Destroy;
end;
procedure TJPEGImage.Assign(Source: TPersistent);
begin
if Source is TJPEGImage then
begin
FImage.Release;
FImage := TJPEGImage(Source).FImage;
FImage.Reference;
if TJPEGImage(Source).FBitmap <> nil then
begin
NewBitmap;
FBitmap.Assign(TJPEGImage(Source).FBitmap);
end;
end
else if Source is TBitmap then
begin
NewImage;
NewBitmap;
FBitmap.Assign(Source);
end
else
inherited Assign(Source);
end;
procedure TJPEGImage.AssignTo(Dest: TPersistent);
begin
if Dest is TBitmap then
Dest.Assign(Bitmap)
else
inherited AssignTo(Dest);
end;
procedure ProgressCallback(const cinfo: jpeg_common_struct);
var
Ticks: Integer;
R: TRect;
temp: Integer;
begin
if (cinfo.progress = nil) or (cinfo.progress^.instance = nil) then Exit;
with cinfo.progress^ do
begin
Ticks := GetTickCount;
if (Ticks - last_time) < 500 then Exit;
temp := last_time;
last_time := Ticks;
if temp = 0 then Exit;
if cinfo.is_decompressor then
with j_decompress_ptr(@cinfo)^ do
begin
R := Rect(0, last_scanline, output_width, output_scanline);
if R.Bottom < last_scanline then
R.Bottom := output_height;
end
else
R := Rect(0,0,0,0);
temp := Integer(Trunc(100.0*(completed_passes + (pass_counter/pass_limit))/total_passes));
if temp = last_pct then Exit;
last_pct := temp;
if cinfo.is_decompressor then
last_scanline := j_decompress_ptr(@cinfo)^.output_scanline;
instance.Progress(instance, psRunning, temp, (R.Bottom - R.Top) >= 4, R, '');
end;
end;
procedure ReleaseContext(var jc: TJPEGContext);
begin
if jc.common.err = nil then Exit;
jpeg_destroy(jc.common);
jc.common.err := nil;
end;
procedure InitDecompressor(Obj: TJPEGImage; var jc: TJPEGContext);
begin
FillChar(jc, sizeof(jc), 0);
jc.err := jpeg_std_error;
jc.common.err := @jc.err;
jpeg_CreateDecompress(jc.d, JPEG_LIB_VERSION, sizeof(jc.d));
with Obj do
try
jc.progress.progress_monitor := @ProgressCallback;
jc.progress.instance := Obj;
jc.common.progress := @jc.progress;
Obj.FImage.FData.Position := 0;
jpeg_stdio_src(jc.d, FImage.FData);
jpeg_read_header(jc.d, TRUE);
jc.d.scale_num := 1;
jc.d.scale_denom := 1 shl Byte(FScale);
jc.d.do_block_smoothing := FSmoothing;
if FGrayscale then jc.d.out_color_space := JCS_GRAYSCALE;
if (PixelFormat = jf8Bit) or (jc.d.out_color_space = JCS_GRAYSCALE) then
begin
jc.d.quantize_colors := True;
jc.d.desired_number_of_colors := 236;
end;
if FPerformance = jpBestSpeed then
begin
jc.d.dct_method := JDCT_IFAST;
jc.d.two_pass_quantize := False;
// jc.d.do_fancy_upsampling := False; !! AV inside jpeglib
jc.d.dither_mode := JDITHER_ORDERED;
end;
jc.FinalDCT := jc.d.dct_method;
jc.FinalTwoPassQuant := jc.d.two_pass_quantize;
jc.FinalDitherMode := jc.d.dither_mode;
if FProgressiveDisplay and jpeg_has_multiple_scans(jc.d) then
begin // save requested settings, reset for fastest on all but last scan
jc.d.enable_2pass_quant := jc.d.two_pass_quantize;
jc.d.dct_method := JDCT_IFAST;
jc.d.two_pass_quantize := False;
jc.d.dither_mode := JDITHER_ORDERED;
jc.d.buffered_image := True;
end;
except
ReleaseContext(jc);
raise;
end;
end;
procedure TJPEGImage.CalcOutputDimensions;
var
jc: TJPEGContext;
begin
if not FNeedRecalc then Exit;
InitDecompressor(Self, jc);
try
jc.common.progress := nil;
jpeg_calc_output_dimensions(jc.d);
// read output dimensions
FScaledWidth := jc.d.output_width;
FScaledHeight := jc.d.output_height;
FProgressiveEncoding := jpeg_has_multiple_scans(jc.d);
finally
ReleaseContext(jc);
end;
end;
procedure TJPEGImage.Changed(Sender: TObject);
begin
inherited Changed(Sender);
end;
procedure TJPEGImage.Compress;
var
LinesWritten, LinesPerCall: Integer;
SrcScanLine: Pointer;
PtrInc: Integer;
jc: TJPEGContext;
Src: TBitmap;
begin
FillChar(jc, sizeof(jc), 0);
jc.err := jpeg_std_error;
jc.common.err := @jc.err;
jpeg_CreateCompress(jc.c, JPEG_LIB_VERSION, sizeof(jc.c));
try
try
jc.progress.progress_monitor := @ProgressCallback;
jc.progress.instance := Self;
jc.common.progress := @jc.progress;
if FImage.FData <> nil then NewImage;
FImage.FData := TMemoryStream.Create;
FImage.FData.Position := 0;
jpeg_stdio_dest(jc.c, FImage.FData);
if (FBitmap = nil) or (FBitmap.Width = 0) or (FBitmap.Height = 0) then Exit;
jc.c.image_width := FBitmap.Width;
FImage.FWidth := FBitmap.Width;
jc.c.image_height := FBitmap.Height;
FImage.FHeight := FBitmap.Height;
jc.c.input_components := 3; // JPEG requires 24bit RGB input
jc.c.in_color_space := JCS_RGB;
Src := TBitmap.Create;
try
Src.Assign(FBitmap);
Src.PixelFormat := pf24bit;
jpeg_set_defaults(jc.c);
jpeg_set_quality(jc.c, FQuality, True);
if FGrayscale then
begin
FImage.FGrayscale := True;
jpeg_set_colorspace(jc.c, JCS_GRAYSCALE);
end;
if ProgressiveEncoding then
jpeg_simple_progression(jc.c);
SrcScanline := Src.ScanLine[0];
PtrInc := Integer(Src.ScanLine[1]) - Integer(SrcScanline);
// if no dword padding required and source bitmap is top-down
if (PtrInc > 0) and ((PtrInc and 3) = 0) then
LinesPerCall := jc.c.image_height // do whole bitmap in one call
else
LinesPerCall := 1; // otherwise spoonfeed one row at a time
Progress(Self, psStarting, 0, False, Rect(0,0,0,0), '');
try
jpeg_start_compress(jc.c, True);
while (jc.c.next_scanline < jc.c.image_height) do
begin
LinesWritten := jpeg_write_scanlines(jc.c, @SrcScanline, LinesPerCall);
Inc(Integer(SrcScanline), PtrInc * LinesWritten);
end;
jpeg_finish_compress(jc.c);
finally
if ExceptObject = nil then
PtrInc := 100
else
PtrInc := 0;
Progress(Self, psEnding, PtrInc, False, Rect(0,0,0,0), '');
end;
finally
Src.Free;
end;
except
on EAbort do // OnProgress can raise EAbort to cancel image save
NewImage; // Throw away any partial jpg data
end;
finally
ReleaseContext(jc);
end;
end;
procedure TJPEGImage.DIBNeeded;
begin
GetBitmap;
end;
procedure TJPEGImage.Draw(ACanvas: TCanvas; const Rect: TRect);
begin
ACanvas.StretchDraw(Rect, Bitmap);
end;
function TJPEGImage.Equals(Graphic: TGraphic): Boolean;
begin
Result := False;
if not (Graphic is TJPEGImage) then Exit;
// Only call the inherited if both ends have compressed data.
// We don't want to set off a compression cycle to create the FData stream
if (FImage.FData <> nil) and (TJPEGImage(Graphic).FImage.FData <> nil) then
Result := inherited Equals(Graphic)
else // FImage is shared if Assign is used to copy between TJPEGImage instances
Result := (FImage = TJPEGImage(Graphic).FImage);
end;
procedure TJPEGImage.FreeBitmap;
begin
FBitmap.Free;
FBitmap := nil;
end;
function BuildPalette(const cinfo: jpeg_decompress_struct): HPalette;
var
Pal: TMaxLogPalette;
I: Integer;
C: Byte;
begin
Pal.palVersion := $300;
Pal.palNumEntries := cinfo.actual_number_of_colors;
if cinfo.out_color_space = JCS_GRAYSCALE then
for I := 0 to Pal.palNumEntries-1 do
begin
C := cinfo.colormap^[0]^[I];
Pal.palPalEntry[I].peRed := C;
Pal.palPalEntry[I].peGreen := C;
Pal.palPalEntry[I].peBlue := C;
Pal.palPalEntry[I].peFlags := 0;
end
else
for I := 0 to Pal.palNumEntries-1 do
begin
Pal.palPalEntry[I].peRed := cinfo.colormap^[2]^[I];
Pal.palPalEntry[I].peGreen := cinfo.colormap^[1]^[I];
Pal.palPalEntry[I].peBlue := cinfo.colormap^[0]^[I];
Pal.palPalEntry[I].peFlags := 0;
end;
Result := CreatePalette(PLogPalette(@Pal)^);
end;
procedure BuildColorMap(var cinfo: jpeg_decompress_struct; P: HPalette);
var
Pal: TMaxLogPalette;
Count, I: Integer;
begin
Count := GetPaletteEntries(P, 0, 256, Pal.palPalEntry);
if Count = 0 then Exit; // jpeg_destroy will free colormap
cinfo.colormap := cinfo.common.mem.alloc_sarray(@cinfo.common, JPOOL_IMAGE, Count, 3);
cinfo.actual_number_of_colors := Count;
for I := 0 to Count-1 do
begin
Byte(cinfo.colormap^[2]^[I]) := Pal.palPalEntry[I].peRed;
Byte(cinfo.colormap^[1]^[I]) := Pal.palPalEntry[I].peGreen;
Byte(cinfo.colormap^[0]^[I]) := Pal.palPalEntry[I].peBlue;
end;
end;
function TJPEGImage.GetBitmap: TBitmap;
var
LinesPerCall, LinesRead: Integer;
DestScanLine: Pointer;
PtrInc: Integer;
jc: TJPEGContext;
GeneratePalette: Boolean;
begin
Result := FBitmap;
if Result <> nil then Exit;
if (FBitmap = nil) then FBitmap := TBitmap.Create;
Result := FBitmap;
GeneratePalette := True;
InitDecompressor(Self, jc);
try
try
// Set the bitmap pixel format
FBitmap.Handle := 0;
if (PixelFormat = jf8Bit) or (jc.d.out_color_space = JCS_GRAYSCALE) then
FBitmap.PixelFormat := pf8bit
else
FBitmap.PixelFormat := pf24bit;
Progress(Self, psStarting, 0, False, Rect(0,0,0,0), '');
try
if (FTempPal <> 0) then
begin
if (FPixelFormat = jf8Bit) then
begin // Generate DIB using assigned palette
BuildColorMap(jc.d, FTempPal);
FBitmap.Palette := CopyPalette(FTempPal); // Keep FTempPal around
GeneratePalette := False;
end
else
begin
DeleteObject(FTempPal);
FTempPal := 0;
end;
end;
jpeg_start_decompress(jc.d);
// Set bitmap width and height
with FBitmap do
begin
Handle := 0;
Width := jc.d.output_width;
Height := jc.d.output_height;
DestScanline := ScanLine[0];
PtrInc := Integer(ScanLine[1]) - Integer(DestScanline);
if (PtrInc > 0) and ((PtrInc and 3) = 0) then
// if no dword padding is required and output bitmap is top-down
LinesPerCall := jc.d.rec_outbuf_height // read multiple rows per call
else
LinesPerCall := 1; // otherwise read one row at a time
end;
if jc.d.buffered_image then
begin // decode progressive scans at low quality, high speed
while jpeg_consume_input(jc.d) <> JPEG_REACHED_EOI do
begin
jpeg_start_output(jc.d, jc.d.input_scan_number);
// extract color palette
if (jc.common.progress^.completed_passes = 0) and (jc.d.colormap <> nil)
and (FBitmap.PixelFormat = pf8bit) and GeneratePalette then
begin
FBitmap.Palette := BuildPalette(jc.d);
PaletteModified := True;
end;
DestScanLine := FBitmap.ScanLine[0];
while (jc.d.output_scanline < jc.d.output_height) do
begin
LinesRead := jpeg_read_scanlines(jc.d, @DestScanline, LinesPerCall);
Inc(Integer(DestScanline), PtrInc * LinesRead);
end;
jpeg_finish_output(jc.d);
end;
// reset options for final pass at requested quality
jc.d.dct_method := jc.FinalDCT;
jc.d.dither_mode := jc.FinalDitherMode;
if jc.FinalTwoPassQuant then
begin
jc.d.two_pass_quantize := True;
jc.d.colormap := nil;
end;
jpeg_start_output(jc.d, jc.d.input_scan_number);
DestScanLine := FBitmap.ScanLine[0];
end;
// build final color palette
if (not jc.d.buffered_image or jc.FinalTwoPassQuant) and
(jc.d.colormap <> nil) and GeneratePalette then
begin
FBitmap.Palette := BuildPalette(jc.d);
PaletteModified := True;
DestScanLine := FBitmap.ScanLine[0];
end;
// final image pass for progressive, first and only pass for baseline
while (jc.d.output_scanline < jc.d.output_height) do
begin
LinesRead := jpeg_read_scanlines(jc.d, @DestScanline, LinesPerCall);
Inc(Integer(DestScanline), PtrInc * LinesRead);
end;
if jc.d.buffered_image then jpeg_finish_output(jc.d);
jpeg_finish_decompress(jc.d);
finally
if ExceptObject = nil then
PtrInc := 100
else
PtrInc := 0;
Progress(Self, psEnding, PtrInc, PaletteModified, Rect(0,0,0,0), '');
// Make sure new palette gets realized, in case OnProgress event didn't.
if PaletteModified then
Changed(Self);
end;
except
on EAbort do ; // OnProgress can raise EAbort to cancel image load
end;
finally
ReleaseContext(jc);
end;
end;
function TJPEGImage.GetEmpty: Boolean;
begin
Result := (FImage.FData = nil) and ((FBitmap = nil) or FBitmap.Empty);
end;
function TJPEGImage.GetGrayscale: Boolean;
begin
Result := FGrayscale or FImage.FGrayscale;
end;
function TJPEGImage.GetPalette: HPalette;
var
DC: HDC;
begin
Result := 0;
if FBitmap <> nil then
Result := FBitmap.Palette
else if FTempPal <> 0 then
Result := FTempPal
else if FPixelFormat = jf24Bit then // check for 8 bit screen
begin
DC := GetDC(0);
if (GetDeviceCaps(DC, BITSPIXEL) * GetDeviceCaps(DC, PLANES)) <= 8 then
begin
FTempPal := CreateHalftonePalette(DC);
Result := FTempPal;
end;
ReleaseDC(0, DC);
end;
end;
function TJPEGImage.GetHeight: Integer;
begin
if FBitmap <> nil then
Result := FBitmap.Height
else if FScale = jsFullSize then
Result := FImage.FHeight
else
begin
CalcOutputDimensions;
Result := FScaledHeight;
end;
end;
function TJPEGImage.GetWidth: Integer;
begin
if FBitmap <> nil then
Result := FBitmap.Width
else if FScale = jsFullSize then
Result := FImage.FWidth
else
begin
CalcOutputDimensions;
Result := FScaledWidth;
end;
end;
procedure TJPEGImage.JPEGNeeded;
begin
if FImage.FData = nil then
Compress;
end;
procedure TJPEGImage.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
begin
//!! check for jpeg clipboard data, mime type image/jpeg
FBitmap.LoadFromClipboardFormat(AFormat, AData, APalette);
end;
procedure TJPEGImage.LoadFromStream(Stream: TStream);
begin
ReadStream(Stream.Size - Stream.Position, Stream);
end;
procedure TJPEGImage.NewBitmap;
begin
FBitmap.Free;
FBitmap := TBitmap.Create;
end;
procedure TJPEGImage.NewImage;
begin
if FImage <> nil then FImage.Release;
FImage := TJPEGData.Create;
FImage.Reference;
end;
procedure TJPEGImage.ReadData(Stream: TStream);
var
Size: Longint;
begin
Stream.Read(Size, SizeOf(Size));
ReadStream(Size, Stream);
end;
procedure TJPEGImage.ReadStream(Size: Longint; Stream: TStream);
var
jerr: jpeg_error_mgr;
cinfo: jpeg_decompress_struct;
begin
NewImage;
FBitmap.Free;
FBitmap := nil;
with FImage do
begin
FData := TMemoryStream.Create;
FData.Size := Size;
Stream.ReadBuffer(FData.Memory^, Size);
if Size > 0 then
begin
jerr := jpeg_std_error; // use local var for thread isolation
cinfo.common.err := @jerr;
jpeg_CreateDecompress(cinfo, JPEG_LIB_VERSION, sizeof(cinfo));
try
FData.Position := 0;
jpeg_stdio_src(cinfo, FData);
jpeg_read_header(cinfo, TRUE);
FWidth := cinfo.image_width;
FHeight := cinfo.image_height;
FGrayscale := cinfo.jpeg_color_space = JCS_GRAYSCALE;
FProgressiveEncoding := jpeg_has_multiple_scans(cinfo);
finally
jpeg_destroy_decompress(cinfo);
end;
end;
end;
PaletteModified := True;
Changed(Self);
end;
procedure TJPEGImage.SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE);
begin
//!! check for jpeg clipboard format, mime type image/jpeg
Bitmap.SaveToClipboardFormat(AFormat, AData, APalette);
end;
procedure TJPEGImage.SaveToStream(Stream: TStream);
begin
JPEGNeeded;
with FImage.FData do
Stream.Write(Memory^, Size);
end;
procedure TJPEGImage.SetGrayscale(Value: Boolean);
begin
if FGrayscale <> Value then
begin
FreeBitmap;
FGrayscale := Value;
PaletteModified := True;
Changed(Self);
end;
end;
procedure TJPEGImage.SetHeight(Value: Integer);
begin
InvalidOperation(SChangeJPGSize);
end;
procedure TJPEGImage.SetPalette(Value: HPalette);
var
SignalChange: Boolean;
begin
if Value <> FTempPal then
begin
SignalChange := (FBitmap <> nil) and (Value <> FBitmap.Palette);
if SignalChange then FreeBitmap;
if FTempPal <> 0 then DeleteObject(FTempPal);
FTempPal := Value;
if SignalChange then
begin
PaletteModified := True;
Changed(Self);
end;
end;
end;
procedure TJPEGImage.SetPerformance(Value: TJPEGPerformance);
begin
if FPerformance <> Value then
begin
FreeBitmap;
FPerformance := Value;
PaletteModified := True;
Changed(Self);
end;
end;
procedure TJPEGImage.SetPixelFormat(Value: TJPEGPixelFormat);
begin
if FPixelFormat <> Value then
begin
FreeBitmap;
FPixelFormat := Value;
PaletteModified := True;
Changed(Self);
end;
end;
procedure TJPEGImage.SetScale(Value: TJPEGScale);
begin
if FScale <> Value then
begin
FreeBitmap;
FScale := Value;
FNeedRecalc := True;
Changed(Self);
end;
end;
procedure TJPEGImage.SetSmoothing(Value: Boolean);
begin
if FSmoothing <> Value then
begin
FreeBitmap;
FSmoothing := Value;
Changed(Self);
end;
end;
procedure TJPEGImage.SetWidth(Value: Integer);
begin
InvalidOperation(SChangeJPGSize);
end;
procedure TJPEGImage.WriteData(Stream: TStream);
var
Size: Longint;
begin
Size := 0;
if Assigned(FImage.FData) then Size := FImage.FData.Size;
Stream.Write(Size, Sizeof(Size));
if Size > 0 then Stream.Write(FImage.FData.Memory^, Size);
end;
procedure InitDefaults;
var
DC: HDC;
begin
DC := GetDC(0);
if (GetDeviceCaps(DC, BITSPIXEL) * GetDeviceCaps(DC, PLANES)) <= 8 then
JPEGDefaults.PixelFormat := jf8Bit
else
JPEGDefaults.PixelFormat := jf24Bit;
ReleaseDC(0, DC);
end;
initialization
InitDefaults;
TPicture.RegisterFileFormat('jpeg', sJPEGImageFile, TJPEGImage);
TPicture.RegisterFileFormat('jpg', sJPEGImageFile, TJPEGImage);
finalization
TPicture.UnregisterGraphicClass(TJPEGImage);
end.