pasjpeg.pas 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  1. unit PasJPeg;
  2. {$I jconfig.inc}
  3. interface
  4. uses
  5. Classes, SysUtils;
  6. type
  7. EJPEG = class(Exception);
  8. JPEG_ProgressMonitor = procedure(Percent: Integer);
  9. procedure LoadJPEG(
  10. {streams:}
  11. const infile, outfile: TStream; inmemory: boolean;
  12. {decompression parameters:}
  13. numcolors: integer;
  14. {progress monitor}
  15. callback: JPEG_ProgressMonitor);
  16. procedure StoreJPEG(
  17. {streams}
  18. const infile, outfile: TStream; inmemory: boolean;
  19. {compression parameters:}
  20. quality: integer;
  21. {progress monitor}
  22. callback: JPEG_ProgressMonitor);
  23. implementation
  24. uses
  25. // WinTypes, Dialogs,
  26. {PASJPG10 library}
  27. jmorecfg,
  28. jpeglib,
  29. jerror,
  30. jdeferr,
  31. jdmarker,
  32. jdmaster,
  33. jdapimin,
  34. jdapistd,
  35. jcparam,
  36. jcapimin,
  37. jcapistd,
  38. jcomapi;
  39. { ---------------------------------------------------------------------- }
  40. { source manager to read compressed data }
  41. { for reference: JDATASRC.PAS in PASJPG10 library }
  42. { ---------------------------------------------------------------------- }
  43. type
  44. my_src_ptr = ^my_source_mgr;
  45. my_source_mgr = record
  46. pub : jpeg_source_mgr; {public fields}
  47. infile : TStream; {source stream}
  48. buffer : JOCTET_FIELD_PTR; {start of buffer}
  49. start_of_file : boolean; {have we gotten any data yet?}
  50. end;
  51. const
  52. INPUT_BUF_SIZE = 4096;
  53. procedure init_source(cinfo : j_decompress_ptr); far;
  54. var
  55. src : my_src_ptr;
  56. begin
  57. src := my_src_ptr(cinfo^.src);
  58. src^.start_of_file := TRUE;
  59. end;
  60. function fill_input_buffer(cinfo : j_decompress_ptr) : boolean; far;
  61. var
  62. src : my_src_ptr;
  63. nbytes : size_t;
  64. begin
  65. src := my_src_ptr(cinfo^.src);
  66. nbytes := src^.infile.Read(src^.buffer^, INPUT_BUF_SIZE);
  67. if (nbytes <= 0) then begin
  68. if (src^.start_of_file) then {Treat empty input file as fatal error}
  69. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EMPTY);
  70. WARNMS(j_common_ptr(cinfo), JWRN_JPEG_EOF);
  71. {Insert a fake EOI marker}
  72. src^.buffer^[0] := JOCTET ($FF);
  73. src^.buffer^[1] := JOCTET (JPEG_EOI);
  74. nbytes := 2;
  75. end;
  76. src^.pub.next_input_byte := JOCTETptr(src^.buffer);
  77. src^.pub.bytes_in_buffer := nbytes;
  78. src^.start_of_file := FALSE;
  79. fill_input_buffer := TRUE;
  80. end;
  81. procedure skip_input_data(cinfo : j_decompress_ptr;
  82. num_bytes : long); far;
  83. var
  84. src : my_src_ptr;
  85. begin
  86. src := my_src_ptr (cinfo^.src);
  87. if (num_bytes > 0) then begin
  88. while (num_bytes > long(src^.pub.bytes_in_buffer)) do begin
  89. Dec(num_bytes, long(src^.pub.bytes_in_buffer));
  90. fill_input_buffer(cinfo);
  91. { note we assume that fill_input_buffer will never return FALSE,
  92. so suspension need not be handled. }
  93. end;
  94. Inc( src^.pub.next_input_byte, size_t(num_bytes) );
  95. Dec( src^.pub.bytes_in_buffer, size_t(num_bytes) );
  96. end;
  97. end;
  98. procedure term_source(cinfo : j_decompress_ptr); far;
  99. begin
  100. { no work necessary here }
  101. end;
  102. procedure jpeg_stream_src(cinfo : j_decompress_ptr; const infile: TStream);
  103. var
  104. src : my_src_ptr;
  105. begin
  106. if (cinfo^.src = nil) then begin {first time for this JPEG object?}
  107. cinfo^.src := jpeg_source_mgr_ptr(
  108. cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_PERMANENT,
  109. SIZEOF(my_source_mgr)) );
  110. src := my_src_ptr (cinfo^.src);
  111. src^.buffer := JOCTET_FIELD_PTR(
  112. cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_PERMANENT,
  113. INPUT_BUF_SIZE * SIZEOF(JOCTET)) );
  114. end;
  115. src := my_src_ptr (cinfo^.src);
  116. {override pub's method pointers}
  117. src^.pub.init_source := init_source;
  118. src^.pub.fill_input_buffer := fill_input_buffer;
  119. src^.pub.skip_input_data := skip_input_data;
  120. src^.pub.resync_to_restart := jpeg_resync_to_restart; {use default method}
  121. src^.pub.term_source := term_source;
  122. {define our fields}
  123. src^.infile := infile;
  124. src^.pub.bytes_in_buffer := 0; {forces fill_input_buffer on first read}
  125. src^.pub.next_input_byte := nil; {until buffer loaded}
  126. end;
  127. { ---------------------------------------------------------------------- }
  128. { destination manager to write compressed data }
  129. { for reference: JDATADST.PAS in PASJPG10 library }
  130. { ---------------------------------------------------------------------- }
  131. type
  132. my_dest_ptr = ^my_destination_mgr;
  133. my_destination_mgr = record
  134. pub : jpeg_destination_mgr; {public fields}
  135. outfile : TStream; {target stream}
  136. buffer : JOCTET_FIELD_PTR; {start of buffer}
  137. end;
  138. const
  139. OUTPUT_BUF_SIZE = 4096;
  140. procedure init_destination(cinfo : j_compress_ptr); far;
  141. var
  142. dest : my_dest_ptr;
  143. begin
  144. dest := my_dest_ptr(cinfo^.dest);
  145. dest^.buffer := JOCTET_FIELD_PTR(
  146. cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE,
  147. OUTPUT_BUF_SIZE * SIZEOF(JOCTET)) );
  148. dest^.pub.next_output_byte := JOCTETptr(dest^.buffer);
  149. dest^.pub.free_in_buffer := OUTPUT_BUF_SIZE;
  150. end;
  151. function empty_output_buffer(cinfo : j_compress_ptr) : boolean; far;
  152. var
  153. dest : my_dest_ptr;
  154. begin
  155. dest := my_dest_ptr(cinfo^.dest);
  156. if (dest^.outfile.Write(dest^.buffer^, OUTPUT_BUF_SIZE)
  157. <> size_t(OUTPUT_BUF_SIZE))
  158. then
  159. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  160. dest^.pub.next_output_byte := JOCTETptr(dest^.buffer);
  161. dest^.pub.free_in_buffer := OUTPUT_BUF_SIZE;
  162. empty_output_buffer := TRUE;
  163. end;
  164. procedure term_destination(cinfo : j_compress_ptr); far;
  165. var
  166. dest : my_dest_ptr;
  167. datacount : size_t;
  168. begin
  169. dest := my_dest_ptr (cinfo^.dest);
  170. datacount := OUTPUT_BUF_SIZE - dest^.pub.free_in_buffer;
  171. {write any data remaining in the buffer}
  172. if (datacount > 0) then
  173. if dest^.outfile.Write(dest^.buffer^, datacount) <> datacount then
  174. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  175. end;
  176. procedure jpeg_stream_dest(cinfo : j_compress_ptr; const outfile: TStream);
  177. var
  178. dest : my_dest_ptr;
  179. begin
  180. if (cinfo^.dest = nil) then begin {first time for this JPEG object?}
  181. cinfo^.dest := jpeg_destination_mgr_ptr(
  182. cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_PERMANENT,
  183. SIZEOF(my_destination_mgr)) );
  184. end;
  185. dest := my_dest_ptr (cinfo^.dest);
  186. {override pub's method pointers}
  187. dest^.pub.init_destination := init_destination;
  188. dest^.pub.empty_output_buffer := empty_output_buffer;
  189. dest^.pub.term_destination := term_destination;
  190. {define our fields}
  191. dest^.outfile := outfile;
  192. end;
  193. { ------------------------------------------------------------------------ }
  194. { Bitmap writing routines }
  195. { for reference: WRBMP.PAS in PASJPG10 library }
  196. { ------------------------------------------------------------------------ }
  197. { NOTE: we always write BMP's in Windows format, no OS/2 formats! }
  198. { however, we read all bitmap flavors (see bitmap reading) }
  199. { ------------------------------------------------------------------------ }
  200. { To support 12-bit JPEG data, we'd have to scale output down to 8 bits.
  201. This is not yet implemented. }
  202. {$ifndef BITS_IN_JSAMPLE_IS_8}
  203. Sorry, this code only copes with 8-bit JSAMPLEs. { deliberate syntax err }
  204. {$endif}
  205. type
  206. BGRptr = ^BGRtype;
  207. BGRtype = packed record
  208. b,g,r : byte;
  209. end;
  210. RGBptr = ^RGBtype;
  211. RGBtype = packed record
  212. r,g,b : JSAMPLE;
  213. end;
  214. bmp_dest_ptr = ^bmp_dest_struct;
  215. bmp_dest_struct = record
  216. outfile : TStream; {Stream to write to}
  217. inmemory : boolean; {keep whole image in memory}
  218. {image info}
  219. data_width : JDIMENSION; {JSAMPLEs per row}
  220. row_width : JDIMENSION; {physical width of one row in the BMP file}
  221. pad_bytes : INT; {number of padding bytes needed per row}
  222. grayscale : boolean; {grayscale or quantized color table ?}
  223. {pixelrow buffer}
  224. buffer : JSAMPARRAY; {pixelrow buffer}
  225. buffer_height : JDIMENSION; {normally, we'll use 1}
  226. {image buffer}
  227. image_buffer : jvirt_sarray_ptr;{needed to reverse row order BMP<>JPG}
  228. image_buffer_height : JDIMENSION; {}
  229. cur_output_row : JDIMENSION; {next row# to write to virtual array}
  230. row_offset : INT32; {position of next row to write to BMP}
  231. end;
  232. procedure write_bmp_header (cinfo : j_decompress_ptr;
  233. dest : bmp_dest_ptr);
  234. {Write a Windows-style BMP file header, including colormap if needed}
  235. var
  236. bmpfileheader : TBitmapFileHeader;
  237. bmpinfoheader : TBitmapInfoHeader;
  238. headersize : INT32;
  239. bits_per_pixel, cmap_entries, num_colors, i : INT;
  240. output_ext_color_map : array[0..255] of record b,g,r,a: byte; end;
  241. begin
  242. {colormap size and total file size}
  243. if (cinfo^.out_color_space = JCS_RGB) then begin
  244. if (cinfo^.quantize_colors) then begin {colormapped RGB}
  245. bits_per_pixel := 8;
  246. cmap_entries := 256;
  247. end else begin {unquantized, full color RGB}
  248. bits_per_pixel := 24;
  249. cmap_entries := 0;
  250. end;
  251. end else begin {grayscale output. We need to fake a 256-entry colormap.}
  252. bits_per_pixel := 8;
  253. cmap_entries := 256;
  254. end;
  255. headersize := SizeOf(TBitmapFileHeader)+SizeOf(TBitmapInfoHeader)+
  256. cmap_entries * 4;
  257. {define headers}
  258. FillChar(bmpfileheader, SizeOf(bmpfileheader), $0);
  259. FillChar(bmpinfoheader, SizeOf(bmpinfoheader), $0);
  260. with bmpfileheader do begin
  261. bfType := $4D42; {BM}
  262. bfSize := headersize + INT32(dest^.row_width) * INT32(cinfo^.output_height);
  263. bfOffBits := headersize;
  264. end;
  265. with bmpinfoheader do begin
  266. biSize := SizeOf(TBitmapInfoHeader);
  267. biWidth := cinfo^.output_width;
  268. biHeight := cinfo^.output_height;
  269. biPlanes := 1;
  270. biBitCount := bits_per_pixel;
  271. if (cinfo^.density_unit = 2) then begin
  272. biXPelsPerMeter := INT32(cinfo^.X_density*100);
  273. biYPelsPerMeter := INT32(cinfo^.Y_density*100);
  274. end;
  275. biClrUsed := cmap_entries;
  276. end;
  277. if dest^.outfile.Write(bmpfileheader, SizeOf(bmpfileheader))
  278. <> size_t(SizeOf(bmpfileheader)) then
  279. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  280. if dest^.outfile.Write(bmpinfoheader, SizeOf(bmpinfoheader))
  281. <> size_t(SizeOf(bmpinfoheader)) then
  282. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  283. {colormap}
  284. if cmap_entries > 0 then begin
  285. num_colors := cinfo^.actual_number_of_colors;
  286. if cinfo^.colormap <> nil then begin
  287. if cinfo^.out_color_components = 3 then
  288. for i := 0 to pred(num_colors) do
  289. with output_ext_color_map[i] do begin
  290. b := GETJSAMPLE(cinfo^.colormap^[2]^[i]);
  291. g := GETJSAMPLE(cinfo^.colormap^[1]^[i]);
  292. r := GETJSAMPLE(cinfo^.colormap^[0]^[i]);
  293. a := 0;
  294. end
  295. else
  296. {grayscale colormap (only happens with grayscale quantization)}
  297. for i := 0 to pred(num_colors) do
  298. with output_ext_color_map[i] do begin
  299. b := GETJSAMPLE(cinfo^.colormap^[0]^[i]);
  300. g := GETJSAMPLE(cinfo^.colormap^[0]^[i]);
  301. r := GETJSAMPLE(cinfo^.colormap^[0]^[i]);
  302. a := 0;
  303. end;
  304. i := num_colors;
  305. end else begin
  306. {if no colormap, must be grayscale data. Generate a linear "map".}
  307. {Nomssi: do not use "num_colors" here, it should be 0}
  308. for i := 0 to pred(256) do
  309. with output_ext_color_map[i] do begin
  310. b := i;
  311. g := i;
  312. r := i;
  313. a := 0;
  314. end;
  315. i := 256;
  316. end;
  317. {pad colormap with zeros to ensure specified number of colormap entries}
  318. if i > cmap_entries then
  319. ERREXIT1(j_common_ptr(cinfo), JERR_TOO_MANY_COLORS, i);
  320. while i < cmap_entries do begin
  321. with output_ext_color_map[i] do begin
  322. b := 0;
  323. g := 0;
  324. r := 0;
  325. a := 0;
  326. end;
  327. Inc(i);
  328. end;
  329. if dest^.outfile.Write(output_ext_color_map, cmap_entries*4)
  330. <> cmap_entries*4 then
  331. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  332. end;
  333. dest^.row_offset := bmpfileheader.bfSize;
  334. end;
  335. procedure write_bmp_pixelrow (cinfo : j_decompress_ptr;
  336. dest : bmp_dest_ptr;
  337. rows_supplied : JDIMENSION);
  338. var
  339. image_ptr : JSAMPARRAY;
  340. inptr, outptr : JSAMPLE_PTR;
  341. BGR : BGRptr;
  342. col,row : JDIMENSION;
  343. pad : int;
  344. begin
  345. if dest^.inmemory then begin
  346. row := dest^.cur_output_row;
  347. Inc(dest^.cur_output_row);
  348. end else begin
  349. row := 0;
  350. Dec(dest^.row_offset, dest^.row_width);
  351. end;
  352. image_ptr := cinfo^.mem^.access_virt_sarray ( j_common_ptr(cinfo),
  353. dest^.image_buffer, row, JDIMENSION (1), TRUE);
  354. inptr := JSAMPLE_PTR(dest^.buffer^[0]);
  355. if not dest^.grayscale then begin
  356. BGR := BGRptr(image_ptr^[0]);
  357. for col := pred(cinfo^.output_width) downto 0 do begin
  358. BGR^.r := inptr^;
  359. Inc(inptr);
  360. BGR^.g := inptr^;
  361. Inc(inptr);
  362. BGR^.b := inptr^;
  363. Inc(inptr);
  364. Inc(BGR);
  365. end;
  366. outptr := JSAMPLE_PTR(BGR);
  367. end else begin
  368. outptr := JSAMPLE_PTR(image_ptr^[0]);
  369. for col := pred(cinfo^.output_width) downto 0 do begin
  370. outptr^ := inptr^;
  371. Inc(outptr);
  372. Inc(inptr);
  373. end;
  374. end;
  375. {zero out the pad bytes}
  376. pad := dest^.pad_bytes;
  377. while (pad > 0) do begin
  378. Dec(pad);
  379. outptr^ := 0;
  380. Inc(outptr);
  381. end;
  382. if not dest^.inmemory then begin
  383. {store row in output stream}
  384. image_ptr := cinfo^.mem^.access_virt_sarray ( j_common_ptr(cinfo),
  385. dest^.image_buffer, 0, JDIMENSION(1), FALSE);
  386. outptr := JSAMPLE_PTR(image_ptr^[0]);
  387. if dest^.outfile.Seek(dest^.row_offset, 0) <> dest^.row_offset then
  388. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  389. if dest^.outfile.Write(outptr^, dest^.row_width) <> dest^.row_width then
  390. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  391. end;
  392. end;
  393. procedure write_bmp_image (cinfo : j_decompress_ptr;
  394. dest : bmp_dest_ptr);
  395. var
  396. row, col : JDIMENSION;
  397. image_ptr : JSAMPARRAY;
  398. data_ptr : JSAMPLE_PTR;
  399. begin
  400. if dest^.inmemory then {write the image data from our virtual array}
  401. for row := cinfo^.output_height downto 1 do begin
  402. image_ptr := cinfo^.mem^.access_virt_sarray( j_common_ptr(cinfo),
  403. dest^.image_buffer, row-1, JDIMENSION(1), FALSE);
  404. data_ptr := JSAMPLE_PTR(image_ptr^[0]);
  405. {Nomssi - This won't work for 12bit samples}
  406. if dest^.outfile.Write(data_ptr^, dest^.row_width) <> dest^.row_width then
  407. ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE);
  408. end;
  409. end;
  410. function jinit_write_bmp (cinfo : j_decompress_ptr;
  411. outfile : TStream;
  412. inmemory : boolean) : bmp_dest_ptr;
  413. var
  414. dest : bmp_dest_ptr;
  415. begin
  416. dest := bmp_dest_ptr (
  417. cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE,
  418. SIZEOF(bmp_dest_struct)) );
  419. dest^.outfile := outfile;
  420. dest^.inmemory := inmemory;
  421. {image info}
  422. jpeg_calc_output_dimensions(cinfo);
  423. dest^.data_width := cinfo^.output_width * cinfo^.output_components;
  424. dest^.row_width := dest^.data_width;
  425. while ((dest^.row_width and 3) <> 0) do
  426. Inc(dest^.row_width);
  427. dest^.pad_bytes := int(dest^.row_width-dest^.data_width);
  428. if (cinfo^.out_color_space = JCS_GRAYSCALE) then
  429. dest^.grayscale := True
  430. else if (cinfo^.out_color_space = JCS_RGB) then
  431. if (cinfo^.quantize_colors) then
  432. dest^.grayscale := True
  433. else
  434. dest^.grayscale := False
  435. else
  436. ERREXIT(j_common_ptr(cinfo), JERR_BMP_COLORSPACE);
  437. {decompress buffer}
  438. dest^.buffer := cinfo^.mem^.alloc_sarray
  439. (j_common_ptr(cinfo), JPOOL_IMAGE, dest^.row_width, JDIMENSION (1));
  440. dest^.buffer_height := 1;
  441. {image buffer}
  442. if inmemory then
  443. dest^.image_buffer_height := cinfo^.output_height
  444. else
  445. dest^.image_buffer_height := 1;
  446. dest^.image_buffer := cinfo^.mem^.request_virt_sarray (
  447. j_common_ptr(cinfo), JPOOL_IMAGE, FALSE, dest^.row_width,
  448. dest^.image_buffer_height, JDIMENSION (1) );
  449. dest^.cur_output_row := 0;
  450. {result}
  451. jinit_write_bmp := dest;
  452. end;
  453. { ------------------------------------------------------------------------ }
  454. { Bitmap reading routines }
  455. { for reference: RDBMP.PAS in PASJPG10 library }
  456. { ------------------------------------------------------------------------ }
  457. type
  458. bmp_source_ptr = ^bmp_source_struct;
  459. bmp_source_struct = record
  460. infile : TStream; {stream to read from}
  461. inmemory : boolean; {keep whole image in memory}
  462. {image info}
  463. bits_per_pixel : INT; {bit depth}
  464. colormap : JSAMPARRAY; {BMP colormap (converted to my format)}
  465. row_width : JDIMENSION; {physical width of one row in the BMP file}
  466. {pixelrow buffer}
  467. buffer : JSAMPARRAY; {pixelrow buffer}
  468. buffer_height : JDIMENSION; {normally, we'll use 1}
  469. {image buffer}
  470. image_buffer : jvirt_sarray_ptr; {needed to reverse order BMP<>JPG}
  471. image_buffer_height : JDIMENSION; {image_height}
  472. cur_input_row : JDIMENSION; {current source row number}
  473. row_offset : INT32; {position of next row to read from BMP}
  474. end;
  475. procedure read_bmp_header (cinfo : j_compress_ptr;
  476. source : bmp_source_ptr);
  477. var
  478. bmpfileheader : TBitmapFileHeader;
  479. bmpcoreheader : TBitmapCoreHeader;
  480. bmpinfoheader : TBitmapInfoHeader;
  481. i, cmap_entrysize : INT;
  482. function read_byte: INT;
  483. {Read next byte from BMP file}
  484. var
  485. c: byte;
  486. begin
  487. if source^.infile.Read(c, 1) <> size_t(1) then
  488. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  489. read_byte := c;
  490. end;
  491. begin
  492. cmap_entrysize := 0; { 0 indicates no colormap }
  493. {bitmap file header:}
  494. if source^.infile.Read(bmpfileheader, SizeOf(bmpfileheader))
  495. <> size_t(SizeOf(bmpfileheader)) then
  496. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  497. if bmpfileheader.bfType <> $4D42 then {'BM'}
  498. ERREXIT(j_common_ptr(cinfo), JERR_BMP_NOT);
  499. {bitmap infoheader: might be 12 bytes (OS/2 1.x), 40 bytes (Windows),
  500. or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which}
  501. if source^.infile.Read(bmpinfoheader, SizeOf(INT32)) <> size_t(SizeOf(INT32)) then
  502. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  503. {OS/2 1.x format}
  504. if bmpinfoheader.biSize = SizeOf(TBitmapCoreHeader) then begin
  505. bmpcoreheader.bcSize := bmpinfoheader.biSize;
  506. if source^.infile.Read(bmpcoreheader.bcWidth, bmpcoreheader.bcSize-SizeOf(INT32))
  507. <> size_t (bmpcoreheader.bcSize-SizeOf(INT32)) then
  508. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  509. bmpinfoheader.biWidth := bmpcoreheader.bcWidth;
  510. bmpinfoheader.biHeight := bmpcoreheader.bcHeight;
  511. bmpinfoheader.biPlanes := bmpcoreheader.bcPlanes;
  512. bmpinfoheader.biBitCount := bmpcoreheader.bcBitCount;
  513. bmpinfoheader.biClrUsed := 0;
  514. source^.bits_per_pixel := bmpinfoheader.biBitCount;
  515. case source^.bits_per_pixel of
  516. 8: begin {colormapped image}
  517. cmap_entrysize := 3; {OS/2 uses RGBTRIPLE colormap}
  518. TRACEMS2( j_common_ptr(cinfo), 1, JTRC_BMP_OS2_MAPPED,
  519. int (bmpinfoheader.biWidth), int(bmpinfoheader.biHeight));
  520. end;
  521. 24: { RGB image }
  522. TRACEMS2( j_common_ptr(cinfo), 1, JTRC_BMP_OS2,
  523. int (bmpinfoheader.biWidth), int(bmpinfoheader.biHeight) );
  524. else
  525. ERREXIT(j_common_ptr(cinfo), JERR_BMP_BADDEPTH);
  526. end;
  527. if bmpinfoheader.biPlanes <> 1 then
  528. ERREXIT(j_common_ptr(cinfo), JERR_BMP_BADPLANES);
  529. end else
  530. {Windows 3.x or OS/2 2.x header, which has additional fields that we ignore }
  531. if (bmpinfoheader.biSize = SizeOf(TBitmapInfoHeader)) or
  532. (bmpinfoheader.biSize = 64) then
  533. begin
  534. if source^.infile.Read(bmpinfoheader.biWidth, SizeOf(bmpinfoheader)-SizeOf(INT32))
  535. <> size_t (SizeOf(bmpinfoheader)-SizeOf(INT32)) then
  536. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  537. if bmpinfoheader.biSize = 64 then
  538. source^.infile.Seek(64-SizeOf(TBitmapInfoHeader), 1);
  539. source^.bits_per_pixel := bmpinfoheader.biBitCount;
  540. case source^.bits_per_pixel of
  541. 8: begin {colormapped image}
  542. cmap_entrysize := 4; {Windows uses RGBQUAD colormap}
  543. TRACEMS2( j_common_ptr(cinfo), 1, JTRC_BMP_MAPPED,
  544. int (bmpinfoheader.biWidth), int(bmpinfoheader.biHeight) );
  545. end;
  546. 24: {RGB image}
  547. TRACEMS2( j_common_ptr(cinfo), 1, JTRC_BMP,
  548. int (bmpinfoheader.biWidth), int(bmpinfoheader.biHeight) );
  549. else
  550. ERREXIT(j_common_ptr(cinfo), JERR_BMP_BADDEPTH);
  551. end;
  552. if (bmpinfoheader.biPlanes <> 1) then
  553. ERREXIT(j_common_ptr(cinfo), JERR_BMP_BADPLANES);
  554. if (bmpinfoheader.biCompression <> 0) then
  555. ERREXIT(j_common_ptr(cinfo), JERR_BMP_COMPRESSED);
  556. if (bmpinfoheader.biXPelsPerMeter > 0) and (bmpinfoheader.biYPelsPerMeter > 0) then
  557. begin
  558. {Set JFIF density parameters from the BMP data}
  559. cinfo^.X_density := bmpinfoheader.biXPelsPerMeter div 100; {100 cm per meter}
  560. cinfo^.Y_density := bmpinfoheader.biYPelsPerMeter div 100;
  561. cinfo^.density_unit := 2; { dots/cm }
  562. end;
  563. end else
  564. ERREXIT(j_common_ptr(cinfo), JERR_BMP_BADHEADER);
  565. {colormap}
  566. if cmap_entrysize > 0 then begin
  567. if bmpinfoheader.biClrUsed <= 0 then
  568. bmpinfoheader.biClrUsed := 256 {assume it's 256}
  569. else
  570. if bmpinfoheader.biClrUsed > 256 then
  571. ERREXIT(j_common_ptr(cinfo), JERR_BMP_BADCMAP);
  572. {allocate colormap}
  573. source^.colormap := cinfo^.mem^.alloc_sarray( j_common_ptr (cinfo),
  574. JPOOL_IMAGE, JDIMENSION(bmpinfoheader.biClrUsed), JDIMENSION (3));
  575. {read it}
  576. case cmap_entrysize of
  577. 3: {BGR format (occurs in OS/2 files)}
  578. for i := 0 to pred(bmpinfoheader.biClrUsed) do begin
  579. source^.colormap^[2]^[i] := JSAMPLE (read_byte);
  580. source^.colormap^[1]^[i] := JSAMPLE (read_byte);
  581. source^.colormap^[0]^[i] := JSAMPLE (read_byte);
  582. end;
  583. 4: {BGR0 format (occurs in MS Windows files)}
  584. for i := 0 to pred(bmpinfoheader.biClrUsed) do begin
  585. source^.colormap^[2]^[i] := JSAMPLE (read_byte);
  586. source^.colormap^[1]^[i] := JSAMPLE (read_byte);
  587. source^.colormap^[0]^[i] := JSAMPLE (read_byte);
  588. read_byte;
  589. end;
  590. else
  591. ERREXIT(j_common_ptr(cinfo), JERR_BMP_BADCMAP);
  592. end;
  593. end;
  594. {initialize bmp_source_struc}
  595. {row width, including padding to 4-byte boundary}
  596. if source^.bits_per_pixel = 24 then
  597. source^.row_width := JDIMENSION(bmpinfoheader.biWidth*3)
  598. else
  599. source^.row_width := JDIMENSION (bmpinfoheader.biWidth);
  600. while ((source^.row_width and 3) <> 0) do
  601. Inc(source^.row_width);
  602. {allocate pixelrow buffer}
  603. source^.buffer := cinfo^.mem^.alloc_sarray( j_common_ptr (cinfo),
  604. JPOOL_IMAGE, JDIMENSION (bmpinfoheader.biWidth*3), JDIMENSION (1) );
  605. source^.buffer_height := 1;
  606. {allocate image buffer}
  607. if source^.inmemory then begin
  608. source^.image_buffer_height := bmpinfoheader.biHeight;
  609. source^.cur_input_row := bmpinfoheader.biHeight;
  610. end else begin
  611. source^.image_buffer_height := 1;
  612. source^.row_offset := bmpfileheader.bfSize;
  613. end;
  614. source^.image_buffer := cinfo^.mem^.request_virt_sarray (
  615. j_common_ptr (cinfo), JPOOL_IMAGE, FALSE, source^.row_width,
  616. JDIMENSION(source^.image_buffer_height), JDIMENSION (1) );
  617. {set decompress parameters}
  618. cinfo^.in_color_space := JCS_RGB;
  619. cinfo^.input_components := 3;
  620. cinfo^.data_precision := 8;
  621. cinfo^.image_width := JDIMENSION (bmpinfoheader.biWidth);
  622. cinfo^.image_height := JDIMENSION (bmpinfoheader.biHeight);
  623. end;
  624. function read_bmp_pixelrow (cinfo : j_compress_ptr;
  625. source : bmp_source_ptr) : JDIMENSION;
  626. { Read one row of pixels:
  627. the image has been read into the image_buffer array, but is otherwise
  628. unprocessed. we must read it out in top-to-bottom row order, and if
  629. it is an 8-bit image, we must expand colormapped pixels to 24bit format. }
  630. var
  631. col, row : JDIMENSION;
  632. image_ptr : JSAMPARRAY;
  633. inptr, outptr : JSAMPLE_PTR;
  634. outptr24 : JSAMPROW;
  635. t : INT;
  636. begin
  637. if source^.inmemory then begin
  638. Dec(source^.cur_input_row);
  639. row := source^.cur_input_row;
  640. end else begin
  641. Dec(source^.row_offset, source^.row_width);
  642. row := 0;
  643. end;
  644. if not source^.inmemory then begin
  645. image_ptr := cinfo^.mem^.access_virt_sarray ( j_common_ptr (cinfo),
  646. source^.image_buffer, row, JDIMENSION (1), TRUE);
  647. inptr := JSAMPLE_PTR(image_ptr^[0]);
  648. if source^.infile.Seek(source^.row_offset, 0) <> source^.row_offset then
  649. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  650. if source^.infile.Read(inptr^, source^.row_width)
  651. <> size_t(source^.row_width) then
  652. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  653. end;
  654. image_ptr := cinfo^.mem^.access_virt_sarray ( j_common_ptr (cinfo),
  655. source^.image_buffer, row, JDIMENSION (1), FALSE);
  656. {}
  657. inptr := JSAMPLE_PTR(image_ptr^[0]);
  658. case source^.bits_per_pixel of
  659. 8: begin
  660. {expand the colormap indexes to real data}
  661. outptr := JSAMPLE_PTR(source^.buffer^[0]);
  662. for col := pred(cinfo^.image_width) downto 0 do begin
  663. t := GETJSAMPLE(inptr^);
  664. Inc(inptr);
  665. outptr^ := source^.colormap^[0]^[t];
  666. Inc(outptr);
  667. outptr^ := source^.colormap^[1]^[t];
  668. Inc(outptr);
  669. outptr^ := source^.colormap^[2]^[t];
  670. Inc(outptr);
  671. end;
  672. end;
  673. 24: begin
  674. outptr24 := source^.buffer^[0];
  675. for col := pred(cinfo^.image_width) downto 0 do begin
  676. outptr24^[2] := inptr^;
  677. Inc(inptr);
  678. outptr24^[1] := inptr^;
  679. Inc(inptr);
  680. outptr24^[0] := inptr^;
  681. Inc(inptr);
  682. Inc(JSAMPLE_PTR(outptr24), 3);
  683. end;
  684. end;
  685. end;
  686. read_bmp_pixelrow := 1;
  687. end;
  688. procedure read_bmp_image(cinfo : j_compress_ptr;
  689. source : bmp_source_ptr);
  690. var
  691. row, col : JDIMENSION;
  692. image_ptr : JSAMPARRAY;
  693. inptr : JSAMPLE_PTR;
  694. begin
  695. if source^.inmemory then
  696. for row := 0 to pred(cinfo^.image_height) do begin
  697. image_ptr := cinfo^.mem^.access_virt_sarray ( j_common_ptr (cinfo),
  698. source^.image_buffer, row, JDIMENSION (1), TRUE);
  699. inptr := JSAMPLE_PTR(image_ptr^[0]);
  700. if source^.infile.Read(inptr^, source^.row_width)
  701. <> size_t(source^.row_width)
  702. then
  703. ERREXIT(j_common_ptr(cinfo), JERR_INPUT_EOF);
  704. end;
  705. end;
  706. function jinit_read_bmp (cinfo : j_compress_ptr;
  707. infile : TStream;
  708. inmemory : boolean) : bmp_source_ptr;
  709. var
  710. source : bmp_source_ptr;
  711. begin
  712. source := bmp_source_ptr (
  713. cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE,
  714. SIZEOF(bmp_source_struct)) );
  715. source^.infile := infile;
  716. source^.inmemory := inmemory;
  717. jinit_read_bmp := source;
  718. end;
  719. { ------------------------------------------------------------------------ }
  720. { JPEG progress monitor support }
  721. { for reference: LIPJPEG.DOC in \JPEG\C directory }
  722. { ------------------------------------------------------------------------ }
  723. type
  724. my_progress_ptr = ^my_progress_mgr;
  725. my_progress_mgr = record
  726. pub : jpeg_progress_mgr;
  727. proc : JPEG_ProgressMonitor;
  728. percent_done : INT;
  729. completed_extra_passes : INT;
  730. total_extra_passes : INT;
  731. end;
  732. procedure progress_monitor(cinfo: j_common_ptr); far;
  733. var
  734. progress : my_progress_ptr;
  735. total_passes : INT;
  736. percent_done : INT;
  737. begin
  738. progress := my_progress_ptr(cinfo^.progress);
  739. total_passes :=
  740. progress^.pub.total_passes + progress^.total_extra_passes;
  741. percent_done :=
  742. ( ((progress^.pub.completed_passes+progress^.completed_extra_passes)*100) +
  743. ((progress^.pub.pass_counter*100) div progress^.pub.pass_limit)
  744. ) div total_passes;
  745. {}
  746. if percent_done <> progress^.percent_done then begin
  747. progress^.percent_done := percent_done;
  748. progress^.proc(percent_done);
  749. end;
  750. end;
  751. procedure jpeg_my_progress(cinfo : j_common_ptr;
  752. progress : my_progress_ptr;
  753. callback : JPEG_ProgressMonitor);
  754. begin
  755. if @callback = nil then
  756. Exit;
  757. {set method}
  758. progress^.pub.progress_monitor := progress_monitor;
  759. {set fields}
  760. progress^.proc := callback;
  761. progress^.percent_done := -1;
  762. progress^.completed_extra_passes := 0;
  763. progress^.total_extra_passes := 0;
  764. {link to cinfo}
  765. cinfo^.progress := @progress^.pub;
  766. end;
  767. procedure jpeg_finish_progress(cinfo : j_common_ptr);
  768. var
  769. progress : my_progress_ptr;
  770. begin
  771. progress := my_progress_ptr(cinfo^.progress);
  772. if progress^.percent_done <> 100 then begin
  773. progress^.percent_done := 100;
  774. progress^.proc(progress^.percent_done);
  775. end;
  776. end;
  777. { ------------------------------------------------------------------------ }
  778. { JPEG error handler }
  779. { for reference: JERROR.PAS in PASJPG10 library }
  780. { LIPJPEG.DOC in \JPEG\C directory }
  781. { NOTE: we have replaced jpeg_std_error because it stores a static }
  782. { message table (JDEFERR.PAS) in the jpeg_message_table field. }
  783. { ------------------------------------------------------------------------ }
  784. type
  785. my_error_ptr = ^my_error_mgr;
  786. my_error_mgr = record
  787. pub: jpeg_error_mgr;
  788. end;
  789. procedure error_exit (cinfo : j_common_ptr); far;
  790. var
  791. buffer : string;
  792. begin
  793. cinfo^.err^.format_message(cinfo, buffer);
  794. raise EJPEG.Create(buffer);
  795. end;
  796. procedure emit_message (cinfo : j_common_ptr; msg_level : int); far;
  797. var
  798. err : jpeg_error_mgr_ptr;
  799. begin
  800. err := cinfo^.err;
  801. if (msg_level < 0) then begin
  802. {It's a warning message. Since corrupt files may generate many warnings,}
  803. {the policy implemented here is to show only the first warning,}
  804. {unless trace_level >= 3}
  805. if (err^.num_warnings = 0) or (err^.trace_level >= 3) then
  806. err^.output_message(cinfo);
  807. {Always count warnings in num_warnings}
  808. Inc( err^.num_warnings );
  809. end else
  810. {It's a trace message. Show it if trace_level >= msg_level}
  811. if (err^.trace_level >= msg_level) then
  812. err^.output_message (cinfo);
  813. end;
  814. procedure output_message (cinfo : j_common_ptr); far;
  815. var
  816. buffer : string;
  817. begin
  818. cinfo^.err^.format_message (cinfo, buffer);
  819. {message dialog}
  820. ShowMessage(buffer);
  821. end;
  822. procedure format_message (cinfo : j_common_ptr; var buffer : string); far;
  823. begin
  824. buffer :=
  825. 'JPEG ERROR -- #' + IntToStr(cinfo^.err^.msg_code);
  826. end;
  827. procedure reset_error_mgr (cinfo : j_common_ptr); far;
  828. begin
  829. cinfo^.err^.num_warnings := 0;
  830. {trace_level is not reset since it is an application-supplied parameter}
  831. cinfo^.err^.msg_code := 0; {may be useful as a flag for "no error"}
  832. end;
  833. function jpeg_my_error (var err : my_error_mgr) : jpeg_error_mgr_ptr;
  834. begin
  835. {methods}
  836. err.pub.error_exit := error_exit;
  837. err.pub.emit_message := emit_message;
  838. err.pub.output_message := output_message;
  839. err.pub.format_message := format_message;
  840. err.pub.reset_error_mgr := reset_error_mgr;
  841. {fields}
  842. err.pub.trace_level := 0; {default := no tracing}
  843. err.pub.num_warnings := 0; {no warnings emitted yet}
  844. err.pub.msg_code := 0; {may be useful as a flag for "no error"}
  845. {message table(s)}
  846. err.pub.jpeg_message_table := nil; {we don't want to use a static table}
  847. err.pub.last_jpeg_message := pred(JMSG_LASTMSGCODE);
  848. err.pub.addon_message_table := nil;
  849. err.pub.first_addon_message := JMSG_NOMESSAGE; {for safety}
  850. err.pub.last_addon_message := JMSG_NOMESSAGE;
  851. {return result}
  852. jpeg_my_error := @err;
  853. end;
  854. { ------------------------------------------------------------------------ }
  855. { load JPEG stream and save as BITMAP stream }
  856. { for reference: DJPEG.PAS in PASJPG10 library }
  857. { ------------------------------------------------------------------------ }
  858. procedure LoadJPEG(const infile, outfile: TStream; inmemory: boolean;
  859. {decompression parameters:}
  860. numcolors: integer;
  861. {progress monitor}
  862. callback: JPEG_ProgressMonitor);
  863. var
  864. cinfo : jpeg_decompress_struct;
  865. err : my_error_mgr;
  866. dest : bmp_dest_ptr;
  867. progress : my_progress_mgr;
  868. num_scanlines : JDIMENSION;
  869. begin
  870. {initialize the JPEG decompression object with default error handling.}
  871. cinfo.err := jpeg_my_error(err);
  872. jpeg_create_decompress(@cinfo);
  873. try
  874. {specify the source of the compressed data}
  875. jpeg_stream_src(@cinfo, infile);
  876. {progress monitor}
  877. jpeg_my_progress(@cinfo, @progress, callback);
  878. {obtain image info from header, set default decompression parameters}
  879. jpeg_read_header(@cinfo, TRUE);
  880. {set parameters for decompression}
  881. if numcolors <> 0 then begin
  882. cinfo.desired_number_of_colors := numcolors;
  883. cinfo.quantize_colors := True;
  884. end;
  885. {...}
  886. {prepare for decompression, initialize internal state}
  887. dest := jinit_write_bmp(@cinfo, outfile, inmemory);
  888. jpeg_start_decompress(@cinfo);
  889. {process data}
  890. write_bmp_header(@cinfo, dest);
  891. while (cinfo.output_scanline < cinfo.output_height) do begin
  892. num_scanlines :=
  893. jpeg_read_scanlines(@cinfo, dest^.buffer, dest^.buffer_height);
  894. write_bmp_pixelrow(@cinfo, dest, num_scanlines);
  895. end;
  896. write_bmp_image(@cinfo, dest);
  897. {finish}
  898. jpeg_finish_decompress(@cinfo);
  899. jpeg_finish_progress(@cinfo);
  900. finally
  901. {destroy}
  902. jpeg_destroy_decompress(@cinfo);
  903. end;
  904. end;
  905. { ------------------------------------------------------------------------ }
  906. { read BITMAP stream and save as JPEG }
  907. { for reference: CJPEG.PAS in PASJPG10 library }
  908. { ------------------------------------------------------------------------ }
  909. procedure StoreJPEG(const infile, outfile: TStream; inmemory: boolean;
  910. {compression parameters:}
  911. quality: INT;
  912. {progress monitor}
  913. callback: JPEG_ProgressMonitor);
  914. var
  915. cinfo : jpeg_compress_struct;
  916. err : my_error_mgr;
  917. source : bmp_source_ptr;
  918. progress : my_progress_mgr;
  919. num_scanlines : JDIMENSION;
  920. begin
  921. {initialize the JPEG compression object with default error handling.}
  922. cinfo.err := jpeg_my_error(err);
  923. jpeg_create_compress(@cinfo);
  924. try
  925. {specify the destination for the compressed data}
  926. jpeg_stream_dest(@cinfo, outfile);
  927. {set jpeg defaults}
  928. cinfo.in_color_space := JCS_RGB; {arbitrary guess}
  929. jpeg_set_defaults(@cinfo);
  930. {progress monitor}
  931. jpeg_my_progress(@cinfo, @progress, callback);
  932. {obtain image info from bitmap header, set default compression parameters}
  933. source := jinit_read_bmp(@cinfo, infile, inmemory);
  934. read_bmp_header(@cinfo, source);
  935. {now we know input colorspace, fix colorspace-dependent defaults}
  936. jpeg_default_colorspace(@cinfo);
  937. {set parameters for compression (most likely only quality)}
  938. jpeg_set_quality(@cinfo, quality, TRUE);
  939. {...}
  940. {prepare for compression, initialize internal state}
  941. jpeg_start_compress(@cinfo, TRUE);
  942. {process data}
  943. read_bmp_image(@cinfo, source);
  944. while (cinfo.next_scanline < cinfo.image_height) do begin
  945. num_scanlines := read_bmp_pixelrow(@cinfo, source);
  946. jpeg_write_scanlines(@cinfo, source^.buffer, num_scanlines);
  947. end;
  948. {finish}
  949. jpeg_finish_compress(@cinfo);
  950. jpeg_finish_progress(@cinfo);
  951. finally
  952. {destroy}
  953. jpeg_destroy_compress(@cinfo);
  954. end;
  955. end;
  956. end.