root / Imaging / ImagingJpeg.pas @ 0:95bd93c28625
History | View | Annotate | Download (17.1 kB)
| 1 | {
|
|---|---|
| 2 | $Id: ImagingJpeg.pas 103 2007-09-15 01:11:14Z galfar $ |
| 3 | Vampyre Imaging Library |
| 4 | by Marek Mauder |
| 5 | http://imaginglib.sourceforge.net |
| 6 | |
| 7 | The contents of this file are used with permission, subject to the Mozilla |
| 8 | Public License Version 1.1 (the "License"); you may not use this file except |
| 9 | in compliance with the License. You may obtain a copy of the License at |
| 10 | http://www.mozilla.org/MPL/MPL-1.1.html |
| 11 | |
| 12 | Software distributed under the License is distributed on an "AS IS" basis, |
| 13 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
| 14 | the specific language governing rights and limitations under the License. |
| 15 | |
| 16 | Alternatively, the contents of this file may be used under the terms of the |
| 17 | GNU Lesser General Public License (the "LGPL License"), in which case the |
| 18 | provisions of the LGPL License are applicable instead of those above. |
| 19 | If you wish to allow use of your version of this file only under the terms |
| 20 | of the LGPL License and not to allow others to use your version of this file |
| 21 | under the MPL, indicate your decision by deleting the provisions above and |
| 22 | replace them with the notice and other provisions required by the LGPL |
| 23 | License. If you do not delete the provisions above, a recipient may use |
| 24 | your version of this file under either the MPL or the LGPL License. |
| 25 | |
| 26 | For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html |
| 27 | } |
| 28 | |
| 29 | { This unit contains image format loader/saver for Jpeg images.}
|
| 30 | unit ImagingJpeg;
|
| 31 | |
| 32 | {$I ImagingOptions.inc}
|
| 33 | |
| 34 | { You can choose which Pascal JpegLib implementation will be used.
|
| 35 | IMJPEGLIB is version bundled with Imaging which works with all supported |
| 36 | compilers and platforms. |
| 37 | PASJPEG is original JpegLib translation or version modified for FPC |
| 38 | (and shipped with it). You can use PASJPEG if this version is already |
| 39 | linked with another part of your program and you don't want to have |
| 40 | two quite large almost the same libraries linked to your exe. |
| 41 | This is the case with Lazarus applications for example.} |
| 42 | |
| 43 | {$DEFINE IMJPEGLIB}
|
| 44 | { $DEFINE PASJPEG}
|
| 45 | |
| 46 | { Automatically use FPC's PasJpeg when compiling with Lazarus.}
|
| 47 | |
| 48 | {$IFDEF LCL}
|
| 49 | { $UNDEF IMJPEGLIB}
|
| 50 | {$DEFINE PASJPEG}
|
| 51 | {$ENDIF}
|
| 52 | |
| 53 | interface
|
| 54 | |
| 55 | uses
|
| 56 | SysUtils, ImagingTypes, Imaging, ImagingColors, |
| 57 | {$IF Defined(IMJPEGLIB)}
|
| 58 | imjpeglib, imjmorecfg, imjcomapi, imjdapimin, |
| 59 | imjdapistd, imjcapimin, imjcapistd, imjdmarker, imjcparam, |
| 60 | {$ELSEIF Defined(PASJPEG)}
|
| 61 | jpeglib, jmorecfg, jcomapi, jdapimin, |
| 62 | jdapistd, jcapimin, jcapistd, jdmarker, jcparam, |
| 63 | {$IFEND}
|
| 64 | ImagingUtility; |
| 65 | |
| 66 | {$IF Defined(FPC) and Defined(PASJPEG)}
|
| 67 | { When using FPC's pasjpeg in FPC the channel order is BGR instead of RGB}
|
| 68 | { $DEFINE RGBSWAPPED} // not needed now apparently |
| 69 | {$IFEND}
|
| 70 | |
| 71 | type
|
| 72 | { Class for loading/saving Jpeg images. Supports load/save of
|
| 73 | 8 bit grayscale and 24 bit RGB images.} |
| 74 | TJpegFileFormat = class(TImageFileFormat)
|
| 75 | private
|
| 76 | FGrayScale: Boolean; |
| 77 | protected
|
| 78 | FQuality: LongInt; |
| 79 | FProgressive: LongBool; |
| 80 | procedure SetJpegIO(const JpegIO: TIOFunctions); virtual; |
| 81 | function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray; |
| 82 | OnlyFirstLevel: Boolean): Boolean; override;
|
| 83 | function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray; |
| 84 | Index: LongInt): Boolean; override;
|
| 85 | procedure ConvertToSupported(var Image: TImageData; |
| 86 | const Info: TImageFormatInfo); override; |
| 87 | public
|
| 88 | constructor Create; override; |
| 89 | function TestFormat(Handle: TImagingHandle): Boolean; override; |
| 90 | procedure CheckOptionsValidity; override; |
| 91 | published
|
| 92 | { Controls Jpeg save compression quality. It is number in range 1..100.
|
| 93 | 1 means small/ugly file, 100 means large/nice file. Accessible trough |
| 94 | ImagingJpegQuality option.} |
| 95 | property Quality: LongInt read FQuality write FQuality; |
| 96 | { If True Jpeg images are saved in progressive format. Accessible trough
|
| 97 | ImagingJpegProgressive option.} |
| 98 | property Progressive: LongBool read FProgressive write FProgressive; |
| 99 | end;
|
| 100 | |
| 101 | implementation
|
| 102 | |
| 103 | const
|
| 104 | SJpegFormatName = 'Joint Photographic Experts Group Image';
|
| 105 | SJpegMasks = '*.jpg,*.jpeg,*.jfif,*.jpe,*.jif';
|
| 106 | JpegSupportedFormats: TImageFormats = [ifR8G8B8, ifGray8]; |
| 107 | JpegDefaultQuality = 90;
|
| 108 | JpegDefaultProgressive = False; |
| 109 | |
| 110 | const
|
| 111 | { Jpeg file identifiers.}
|
| 112 | JpegMagic: TChar2 = #$FF#$D8; |
| 113 | JFIFSignature: TChar4 = 'JFIF';
|
| 114 | EXIFSignature: TChar4 = 'Exif';
|
| 115 | BufferSize = 16384;
|
| 116 | |
| 117 | type
|
| 118 | TJpegContext = record
|
| 119 | case Byte of |
| 120 | 0: (common: jpeg_common_struct);
|
| 121 | 1: (d: jpeg_decompress_struct);
|
| 122 | 2: (c: jpeg_compress_struct);
|
| 123 | end;
|
| 124 | |
| 125 | TSourceMgr = record
|
| 126 | Pub: jpeg_source_mgr; |
| 127 | Input: TImagingHandle; |
| 128 | Buffer: JOCTETPTR; |
| 129 | StartOfFile: Boolean; |
| 130 | end;
|
| 131 | PSourceMgr = ^TSourceMgr; |
| 132 | |
| 133 | TDestMgr = record
|
| 134 | Pub: jpeg_destination_mgr; |
| 135 | Output: TImagingHandle; |
| 136 | Buffer: JOCTETPTR; |
| 137 | end;
|
| 138 | PDestMgr = ^TDestMgr; |
| 139 | |
| 140 | var
|
| 141 | JIO: TIOFunctions; |
| 142 | |
| 143 | |
| 144 | { Intenal unit jpeglib support functions }
|
| 145 | |
| 146 | procedure JpegError(CurInfo: j_common_ptr);
|
| 147 | begin
|
| 148 | end;
|
| 149 | |
| 150 | procedure EmitMessage(CurInfo: j_common_ptr; msg_level: Integer);
|
| 151 | begin
|
| 152 | end;
|
| 153 | |
| 154 | procedure OutputMessage(CurInfo: j_common_ptr);
|
| 155 | begin
|
| 156 | end;
|
| 157 | |
| 158 | procedure FormatMessage(CurInfo: j_common_ptr; var buffer: string); |
| 159 | begin
|
| 160 | end;
|
| 161 | |
| 162 | procedure ResetErrorMgr(CurInfo: j_common_ptr);
|
| 163 | begin
|
| 164 | CurInfo^.err^.num_warnings := 0;
|
| 165 | CurInfo^.err^.msg_code := 0;
|
| 166 | end;
|
| 167 | |
| 168 | var
|
| 169 | JpegErrorRec: jpeg_error_mgr = ( |
| 170 | error_exit: JpegError; |
| 171 | emit_message: EmitMessage; |
| 172 | output_message: OutputMessage; |
| 173 | format_message: FormatMessage; |
| 174 | reset_error_mgr: ResetErrorMgr); |
| 175 | |
| 176 | procedure ReleaseContext(var jc: TJpegContext); |
| 177 | begin
|
| 178 | if jc.common.err = nil then |
| 179 | Exit; |
| 180 | jpeg_destroy(@jc.common); |
| 181 | jpeg_destroy_decompress(@jc.d); |
| 182 | jpeg_destroy_compress(@jc.c); |
| 183 | jc.common.err := nil;
|
| 184 | end;
|
| 185 | |
| 186 | procedure InitSource(cinfo: j_decompress_ptr);
|
| 187 | begin
|
| 188 | PSourceMgr(cinfo.src).StartOfFile := True; |
| 189 | end;
|
| 190 | |
| 191 | function FillInputBuffer(cinfo: j_decompress_ptr): Boolean;
|
| 192 | var
|
| 193 | NBytes: LongInt; |
| 194 | Src: PSourceMgr; |
| 195 | begin
|
| 196 | Src := PSourceMgr(cinfo.src); |
| 197 | NBytes := JIO.Read(Src.Input, Src.Buffer, BufferSize); |
| 198 | |
| 199 | if NBytes <= 0 then |
| 200 | begin
|
| 201 | PChar(Src.Buffer)[0] := #$FF; |
| 202 | PChar(Src.Buffer)[1] := Char(JPEG_EOI);
|
| 203 | NBytes := 2;
|
| 204 | end;
|
| 205 | Src.Pub.next_input_byte := Src.Buffer; |
| 206 | Src.Pub.bytes_in_buffer := NBytes; |
| 207 | Src.StartOfFile := False; |
| 208 | Result := True; |
| 209 | end;
|
| 210 | |
| 211 | procedure SkipInputData(cinfo: j_decompress_ptr; num_bytes: LongInt);
|
| 212 | var
|
| 213 | Src: PSourceMgr; |
| 214 | begin
|
| 215 | Src := PSourceMgr(cinfo.src); |
| 216 | if num_bytes > 0 then |
| 217 | begin
|
| 218 | while num_bytes > Src.Pub.bytes_in_buffer do |
| 219 | begin
|
| 220 | Dec(num_bytes, Src.Pub.bytes_in_buffer); |
| 221 | FillInputBuffer(cinfo); |
| 222 | end;
|
| 223 | Src.Pub.next_input_byte := @PByteArray(Src.Pub.next_input_byte)[num_bytes]; |
| 224 | // Inc(LongInt(Src.Pub.next_input_byte), num_bytes);
|
| 225 | Dec(Src.Pub.bytes_in_buffer, num_bytes); |
| 226 | end;
|
| 227 | end;
|
| 228 | |
| 229 | procedure TermSource(cinfo: j_decompress_ptr);
|
| 230 | var
|
| 231 | Src: PSourceMgr; |
| 232 | begin
|
| 233 | Src := PSourceMgr(cinfo.src); |
| 234 | // Move stream position back just after EOI marker so that more that one
|
| 235 | // JPEG images can be loaded from one stream
|
| 236 | JIO.Seek(Src.Input, -Src.Pub.bytes_in_buffer, smFromCurrent); |
| 237 | end;
|
| 238 | |
| 239 | procedure JpegStdioSrc(var cinfo: jpeg_decompress_struct; Handle: |
| 240 | TImagingHandle); |
| 241 | var
|
| 242 | Src: PSourceMgr; |
| 243 | begin
|
| 244 | if cinfo.src = nil then |
| 245 | begin
|
| 246 | cinfo.src := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_PERMANENT, |
| 247 | SizeOf(TSourceMgr)); |
| 248 | Src := PSourceMgr(cinfo.src); |
| 249 | Src.Buffer := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_PERMANENT, |
| 250 | BufferSize * SizeOf(JOCTET)); |
| 251 | end;
|
| 252 | Src := PSourceMgr(cinfo.src); |
| 253 | Src.Pub.init_source := InitSource; |
| 254 | Src.Pub.fill_input_buffer := FillInputBuffer; |
| 255 | Src.Pub.skip_input_data := SkipInputData; |
| 256 | Src.Pub.resync_to_restart := jpeg_resync_to_restart; |
| 257 | Src.Pub.term_source := TermSource; |
| 258 | Src.Input := Handle; |
| 259 | Src.Pub.bytes_in_buffer := 0;
|
| 260 | Src.Pub.next_input_byte := nil;
|
| 261 | end;
|
| 262 | |
| 263 | procedure InitDest(cinfo: j_compress_ptr);
|
| 264 | var
|
| 265 | Dest: PDestMgr; |
| 266 | begin
|
| 267 | Dest := PDestMgr(cinfo.dest); |
| 268 | Dest.Pub.next_output_byte := Dest.Buffer; |
| 269 | Dest.Pub.free_in_buffer := BufferSize; |
| 270 | end;
|
| 271 | |
| 272 | function EmptyOutput(cinfo: j_compress_ptr): Boolean;
|
| 273 | var
|
| 274 | Dest: PDestMgr; |
| 275 | begin
|
| 276 | Dest := PDestMgr(cinfo.dest); |
| 277 | JIO.Write(Dest.Output, Dest.Buffer, BufferSize); |
| 278 | Dest.Pub.next_output_byte := Dest.Buffer; |
| 279 | Dest.Pub.free_in_buffer := BufferSize; |
| 280 | Result := True; |
| 281 | end;
|
| 282 | |
| 283 | procedure TermDest(cinfo: j_compress_ptr);
|
| 284 | var
|
| 285 | Dest: PDestMgr; |
| 286 | DataCount: LongInt; |
| 287 | begin
|
| 288 | Dest := PDestMgr(cinfo.dest); |
| 289 | DataCount := BufferSize - Dest.Pub.free_in_buffer; |
| 290 | if DataCount > 0 then |
| 291 | JIO.Write(Dest.Output, Dest.Buffer, DataCount); |
| 292 | end;
|
| 293 | |
| 294 | procedure JpegStdioDest(var cinfo: jpeg_compress_struct; Handle: |
| 295 | TImagingHandle); |
| 296 | var
|
| 297 | Dest: PDestMgr; |
| 298 | begin
|
| 299 | if cinfo.dest = nil then |
| 300 | cinfo.dest := cinfo.mem.alloc_small(j_common_ptr(@cinfo), |
| 301 | JPOOL_PERMANENT, SizeOf(TDestMgr)); |
| 302 | Dest := PDestMgr(cinfo.dest); |
| 303 | Dest.Buffer := cinfo.mem.alloc_small(j_common_ptr(@cinfo), JPOOL_IMAGE, |
| 304 | BufferSize * SIZEOF(JOCTET)); |
| 305 | Dest.Pub.init_destination := InitDest; |
| 306 | Dest.Pub.empty_output_buffer := EmptyOutput; |
| 307 | Dest.Pub.term_destination := TermDest; |
| 308 | Dest.Output := Handle; |
| 309 | end;
|
| 310 | |
| 311 | procedure InitDecompressor(Handle: TImagingHandle; var jc: TJpegContext); |
| 312 | begin
|
| 313 | FillChar(jc, sizeof(jc), 0);
|
| 314 | jc.common.err := @JpegErrorRec; |
| 315 | jpeg_CreateDecompress(@jc.d, JPEG_LIB_VERSION, sizeof(jc.d)); |
| 316 | JpegStdioSrc(jc.d, Handle); |
| 317 | jpeg_read_header(@jc.d, True); |
| 318 | jc.d.scale_num := 1;
|
| 319 | jc.d.scale_denom := 1;
|
| 320 | jc.d.do_block_smoothing := True; |
| 321 | if jc.d.out_color_space = JCS_GRAYSCALE then |
| 322 | begin
|
| 323 | jc.d.quantize_colors := True; |
| 324 | jc.d.desired_number_of_colors := 256;
|
| 325 | end;
|
| 326 | end;
|
| 327 | |
| 328 | procedure InitCompressor(Handle: TImagingHandle; var jc: TJpegContext; |
| 329 | Saver: TJpegFileFormat); |
| 330 | begin
|
| 331 | FillChar(jc, sizeof(jc), 0);
|
| 332 | jc.common.err := @JpegErrorRec; |
| 333 | jpeg_CreateCompress(@jc.c, JPEG_LIB_VERSION, sizeof(jc.c)); |
| 334 | JpegStdioDest(jc.c, Handle); |
| 335 | jpeg_set_defaults(@jc.c); |
| 336 | jpeg_set_quality(@jc.c, Saver.FQuality, True); |
| 337 | if Saver.FGrayScale then |
| 338 | jpeg_set_colorspace(@jc.c, JCS_GRAYSCALE) |
| 339 | else
|
| 340 | jpeg_set_colorspace(@jc.c, JCS_YCbCr); |
| 341 | if Saver.FProgressive then |
| 342 | jpeg_simple_progression(@jc.c); |
| 343 | end;
|
| 344 | |
| 345 | { TJpegFileFormat class implementation }
|
| 346 | |
| 347 | constructor TJpegFileFormat.Create;
|
| 348 | begin
|
| 349 | inherited Create;
|
| 350 | FName := SJpegFormatName; |
| 351 | FCanLoad := True; |
| 352 | FCanSave := True; |
| 353 | FIsMultiImageFormat := False; |
| 354 | FSupportedFormats := JpegSupportedFormats; |
| 355 | |
| 356 | FQuality := JpegDefaultQuality; |
| 357 | FProgressive := JpegDefaultProgressive; |
| 358 | |
| 359 | AddMasks(SJpegMasks); |
| 360 | RegisterOption(ImagingJpegQuality, @FQuality); |
| 361 | RegisterOption(ImagingJpegProgressive, @FProgressive); |
| 362 | end;
|
| 363 | |
| 364 | procedure TJpegFileFormat.CheckOptionsValidity;
|
| 365 | begin
|
| 366 | // Check if option values are valid
|
| 367 | if not (FQuality in [1..100]) then |
| 368 | FQuality := JpegDefaultQuality; |
| 369 | end;
|
| 370 | |
| 371 | function TJpegFileFormat.LoadData(Handle: TImagingHandle;
|
| 372 | var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
|
| 373 | var
|
| 374 | PtrInc, LinesPerCall, LinesRead, I: Integer; |
| 375 | Dest: PByte; |
| 376 | jc: TJpegContext; |
| 377 | Info: TImageFormatInfo; |
| 378 | Format: TImageFormat; |
| 379 | Col32: PColor32Rec; |
| 380 | {$IFDEF RGBSWAPPED}
|
| 381 | I: LongInt; |
| 382 | Pix: PColor24Rec; |
| 383 | {$ENDIF}
|
| 384 | begin
|
| 385 | // Copy IO functions to global var used in JpegLib callbacks
|
| 386 | SetJpegIO(GetIO); |
| 387 | SetLength(Images, 1);
|
| 388 | with JIO, Images[0] do |
| 389 | try
|
| 390 | InitDecompressor(Handle, jc); |
| 391 | case jc.d.out_color_space of |
| 392 | JCS_GRAYSCALE: Format := ifGray8; |
| 393 | JCS_RGB: Format := ifR8G8B8; |
| 394 | JCS_CMYK: Format := ifA8R8G8B8; |
| 395 | end;
|
| 396 | NewImage(jc.d.image_width, jc.d.image_height, Format, Images[0]);
|
| 397 | jpeg_start_decompress(@jc.d); |
| 398 | GetImageFormatInfo(Format, Info); |
| 399 | PtrInc := Width * Info.BytesPerPixel; |
| 400 | LinesPerCall := 1;
|
| 401 | Dest := Bits; |
| 402 | |
| 403 | while jc.d.output_scanline < jc.d.output_height do |
| 404 | begin
|
| 405 | LinesRead := jpeg_read_scanlines(@jc.d, @Dest, LinesPerCall); |
| 406 | {$IFDEF RGBSWAPPED}
|
| 407 | if Format = ifR8G8B8 then |
| 408 | begin
|
| 409 | Pix := PColor24Rec(Dest); |
| 410 | for I := 0 to Width - 1 do |
| 411 | begin
|
| 412 | SwapValues(Pix.R, Pix.B); |
| 413 | Inc(Pix); |
| 414 | end;
|
| 415 | end;
|
| 416 | {$ENDIF}
|
| 417 | Inc(Dest, PtrInc * LinesRead); |
| 418 | end;
|
| 419 | |
| 420 | if jc.d.out_color_space = JCS_CMYK then |
| 421 | begin
|
| 422 | Col32 := Bits; |
| 423 | // Translate from CMYK to RGB
|
| 424 | for I := 0 to Width * Height - 1 do |
| 425 | begin
|
| 426 | CMYKToRGB(255 - Col32.B, 255 - Col32.G, 255 - Col32.R, 255 - Col32.A, |
| 427 | Col32.R, Col32.G, Col32.B); |
| 428 | Col32.A := 255;
|
| 429 | Inc(Col32); |
| 430 | end;
|
| 431 | end;
|
| 432 | |
| 433 | jpeg_finish_output(@jc.d); |
| 434 | jpeg_finish_decompress(@jc.d); |
| 435 | Result := True; |
| 436 | finally
|
| 437 | ReleaseContext(jc); |
| 438 | end;
|
| 439 | end;
|
| 440 | |
| 441 | function TJpegFileFormat.SaveData(Handle: TImagingHandle;
|
| 442 | const Images: TDynImageDataArray; Index: LongInt): Boolean;
|
| 443 | var
|
| 444 | PtrInc, LinesWritten: LongInt; |
| 445 | Src, Line: PByte; |
| 446 | jc: TJpegContext; |
| 447 | ImageToSave: TImageData; |
| 448 | Info: TImageFormatInfo; |
| 449 | MustBeFreed: Boolean; |
| 450 | {$IFDEF RGBSWAPPED}
|
| 451 | I: LongInt; |
| 452 | Pix: PColor24Rec; |
| 453 | {$ENDIF}
|
| 454 | begin
|
| 455 | Result := False; |
| 456 | // Copy IO functions to global var used in JpegLib callbacks
|
| 457 | SetJpegIO(GetIO); |
| 458 | // Makes image to save compatible with Jpeg saving capabilities
|
| 459 | if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then |
| 460 | with JIO, ImageToSave do |
| 461 | try
|
| 462 | GetImageFormatInfo(Format, Info); |
| 463 | FGrayScale := Format = ifGray8; |
| 464 | InitCompressor(Handle, jc, Self); |
| 465 | jc.c.image_width := Width; |
| 466 | jc.c.image_height := Height; |
| 467 | if FGrayScale then |
| 468 | begin
|
| 469 | jc.c.input_components := 1;
|
| 470 | jc.c.in_color_space := JCS_GRAYSCALE; |
| 471 | end
|
| 472 | else
|
| 473 | begin
|
| 474 | jc.c.input_components := 3;
|
| 475 | jc.c.in_color_space := JCS_RGB; |
| 476 | end;
|
| 477 | |
| 478 | PtrInc := Width * Info.BytesPerPixel; |
| 479 | Src := Bits; |
| 480 | |
| 481 | {$IFDEF RGBSWAPPED}
|
| 482 | GetMem(Line, PtrInc); |
| 483 | {$ENDIF}
|
| 484 | |
| 485 | jpeg_start_compress(@jc.c, True); |
| 486 | while (jc.c.next_scanline < jc.c.image_height) do |
| 487 | begin
|
| 488 | {$IFDEF RGBSWAPPED}
|
| 489 | if Format = ifR8G8B8 then |
| 490 | begin
|
| 491 | Move(Src^, Line^, PtrInc); |
| 492 | Pix := PColor24Rec(Line); |
| 493 | for I := 0 to Width - 1 do |
| 494 | begin
|
| 495 | SwapValues(Pix.R, Pix.B); |
| 496 | Inc(Pix, 1);
|
| 497 | end;
|
| 498 | end;
|
| 499 | {$ELSE}
|
| 500 | Line := Src; |
| 501 | {$ENDIF}
|
| 502 | |
| 503 | LinesWritten := jpeg_write_scanlines(@jc.c, @Line, 1);
|
| 504 | Inc(Src, PtrInc * LinesWritten); |
| 505 | end;
|
| 506 | |
| 507 | jpeg_finish_compress(@jc.c); |
| 508 | Result := True; |
| 509 | finally
|
| 510 | ReleaseContext(jc); |
| 511 | if MustBeFreed then |
| 512 | FreeImage(ImageToSave); |
| 513 | {$IFDEF RGBSWAPPED}
|
| 514 | FreeMem(Line); |
| 515 | {$ENDIF}
|
| 516 | end;
|
| 517 | end;
|
| 518 | |
| 519 | procedure TJpegFileFormat.ConvertToSupported(var Image: TImageData; |
| 520 | const Info: TImageFormatInfo);
|
| 521 | begin
|
| 522 | if Info.HasGrayChannel then |
| 523 | ConvertImage(Image, ifGray8) |
| 524 | else
|
| 525 | ConvertImage(Image, ifR8G8B8); |
| 526 | end;
|
| 527 | |
| 528 | function TJpegFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
|
| 529 | var
|
| 530 | ReadCount: LongInt; |
| 531 | ID: array[0..9] of Char; |
| 532 | begin
|
| 533 | Result := False; |
| 534 | if Handle <> nil then |
| 535 | with GetIO do |
| 536 | begin
|
| 537 | FillChar(ID, SizeOf(ID), 0);
|
| 538 | ReadCount := Read(Handle, @ID, SizeOf(ID));
|
| 539 | Seek(Handle, -ReadCount, smFromCurrent); |
| 540 | Result := (ReadCount = SizeOf(ID)) and
|
| 541 | CompareMem(@ID, @JpegMagic, SizeOf(JpegMagic)); |
| 542 | end;
|
| 543 | end;
|
| 544 | |
| 545 | procedure TJpegFileFormat.SetJpegIO(const JpegIO: TIOFunctions); |
| 546 | begin
|
| 547 | JIO := JpegIO; |
| 548 | end;
|
| 549 | |
| 550 | initialization
|
| 551 | RegisterImageFileFormat(TJpegFileFormat); |
| 552 | |
| 553 | {
|
| 554 | File Notes: |
| 555 | |
| 556 | -- TODOS ---------------------------------------------------- |
| 557 | - nothing now |
| 558 | |
| 559 | -- 0.24.1 Changes/Bug Fixes --------------------------------- |
| 560 | - Fixed loading of CMYK jpeg images. Could cause heap corruption |
| 561 | and loaded image looked wrong. |
| 562 | |
| 563 | -- 0.23 Changes/Bug Fixes ----------------------------------- |
| 564 | - Removed JFIF/EXIF detection from TestFormat. Found JPEGs |
| 565 | with different headers (Lavc) which weren't recognized. |
| 566 | |
| 567 | -- 0.21 Changes/Bug Fixes ----------------------------------- |
| 568 | - MakeCompatible method moved to base class, put ConvertToSupported here. |
| 569 | GetSupportedFormats removed, it is now set in constructor. |
| 570 | - Made public properties for options registered to SetOption/GetOption |
| 571 | functions. |
| 572 | - Changed extensions to filename masks. |
| 573 | - Changed SaveData, LoadData, and MakeCompatible methods according |
| 574 | to changes in base class in Imaging unit. |
| 575 | - Changes in TestFormat, now reads JFIF and EXIF signatures too. |
| 576 | |
| 577 | -- 0.19 Changes/Bug Fixes ----------------------------------- |
| 578 | - input position is now set correctly to the end of the image |
| 579 | after loading is done. Loading of sequence of JPEG files stored in |
| 580 | single stream works now |
| 581 | - when loading and saving images in FPC with PASJPEG read and |
| 582 | blue channels are swapped to have the same chanel order as IMJPEGLIB |
| 583 | - you can now choose between IMJPEGLIB and PASJPEG implementations |
| 584 | |
| 585 | -- 0.17 Changes/Bug Fixes ----------------------------------- |
| 586 | - added SetJpegIO method which is used by JNG image format |
| 587 | } |
| 588 | end.
|
| 589 |