123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 |
- {*******************************************************}
- { }
- { Delphi Runtime Library }
- { JPEG Image Compression/Decompression Unit }
- { }
- { Copyright (c) 1997 Borland International }
- { Copyright (c) 1998 Jacques Nomssi Nzali }
- { }
- {*******************************************************}
- unit jpeg;
- interface
- {$I jconfig.inc}
- {$ifndef Delphi_Stream}
- Define "Delphi_Stream" in jconfig.inc - deliberate syntax error.
- {$endif}
- 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,
- jmorecfg, jerror, jpeglib, jcomapi, jdmaster, jdapistd,
- jdatadst, jcparam, jcapimin, jcapistd, jdapimin, jdatasrc;
- { 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. }
- { Error handler }
- { Progress monitor object }
- type
- new_progress_mgr_ptr = ^new_progress_mgr;
- new_progress_mgr = record
- pub : jpeg_progress_mgr;
- { extra Delphi info }
- instance: TJPEGImage; { ptr to current TJPEGImage object }
- last_pass: Integer;
- last_pct: Integer;
- last_time: Integer;
- last_scanline: Integer;
- end;
- TJPEGContext = record
- err: jpeg_error_mgr;
- progress: new_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;
- 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); far;
- begin
- { -- !! }
- end;
- procedure OutputMessage(cinfo: j_common_ptr); far;
- begin
- { -- !! }
- end;
- procedure FormatMessage(cinfo: j_common_ptr; var buffer: string); far;
- 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;
- progress : new_progress_mgr_ptr;
- begin
- progress := new_progress_mgr_ptr(cinfo.progress);
- if (progress = nil) or (progress.instance = nil) then Exit;
- with progress^,pub 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 := 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.pub.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.pub.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 := (Graphic is TJPEGImage) and
- (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.mem.alloc_sarray(j_common_ptr(@cinfo), 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.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
- if FTempPal <> 0 then DeleteObject(FTempPal); { Memory leak -- fix }
- 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;
- 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.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;
- 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('jpg', 'JPEG Image File', TJPEGImage);
- TPicture.RegisterFileFormat('jpeg', 'JPEG Image File', TJPEGImage);
- finalization
- TPicture.UnregisterGraphicClass(TJPEGImage);
- end.
|