rdswitch.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. Unit RdSwitch;
  2. { This file contains routines to process some of cjpeg's more complicated
  3. command-line switches. Switches processed here are:
  4. -qtables file Read quantization tables from text file
  5. -scans file Read scan script from text file
  6. -qslots N[,N,...] Set component quantization table selectors
  7. -sample HxV[,HxV,...] Set component sampling factors }
  8. { Original: rdswitch.c ; Copyright (C) 1991-1996, Thomas G. Lane. }
  9. interface
  10. {$I jconfig.inc}
  11. uses
  12. cdjpeg, { Common decls for cjpeg/djpeg applications }
  13. {ctype,} { to declare isdigit(), isspace() }
  14. jinclude,
  15. jmorecfg,
  16. jcparam,
  17. jpeglib;
  18. {GLOBAL}
  19. function set_quant_slots (cinfo : j_compress_ptr; argtxt : string) : boolean;
  20. { Process a quantization-table-selectors parameter string, of the form
  21. N[,N,...]
  22. If there are more components than parameters, the last value is re0licated.
  23. }
  24. {GLOBAL}
  25. function set_sample_factors (cinfo : j_compress_ptr;
  26. argtxt : string) : boolean;
  27. { Process a sample-factors parameter string, of the form
  28. HxV[,HxV,...]
  29. If there are more components than parameters, "1x1" is assumed for the rest.
  30. }
  31. {GLOBAL}
  32. function read_quant_tables (cinfo : j_compress_ptr;
  33. const filename : string;
  34. scale_factor : int;
  35. force_baseline : boolean) : boolean;
  36. { Read a set of quantization tables from the specified file.
  37. The file is plain ASCII text: decimal numbers with whitespace between.
  38. Comments preceded by '#' may be included in the file.
  39. There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
  40. The tables are implicitly numbered 0,1,etc.
  41. NOTE: does not affect the qslots mapping, which will default to selecting
  42. table 0 for luminance (or primary) components, 1 for chrominance components.
  43. You must use -qslots if you want a different component->table mapping. }
  44. {GLOBAL}
  45. function read_scan_script (cinfo : j_compress_ptr;
  46. const filename : string) : boolean;
  47. { Read a scan script from the specified text file.
  48. Each entry in the file defines one scan to be emitted.
  49. Entries are separated by semicolons ';'.
  50. An entry contains one to four component indexes,
  51. optionally followed by a colon ':' and four progressive-JPEG parameters.
  52. The component indexes denote which component(s) are to be transmitted
  53. in the current scan. The first component has index 0.
  54. Sequential JPEG is used if the progressive-JPEG parameters are omitted.
  55. The file is free format text: any whitespace may appear between numbers
  56. and the ':' and ';' punctuation marks. Also, other punctuation (such
  57. as commas or dashes) can be placed between numbers if desired.
  58. Comments preceded by '#' may be included in the file.
  59. Note: we do very little validity checking here;
  60. jcmaster.c will validate the script parameters. }
  61. implementation
  62. uses
  63. fcache;
  64. const
  65. BLANK = ' ';
  66. TAB = ^I; { #9 }
  67. CR = #13; { ^M }
  68. LF = #10; { }
  69. {LOCAL}
  70. function text_getc (var fc : Cache) : char;
  71. { Read next char, skipping over any comments (# to end of line) }
  72. { A comment/newline sequence is returned as a newline }
  73. var
  74. ch : char; {register }
  75. begin
  76. ch := char(fc_GetC(fc));
  77. if (ch = '#') then
  78. repeat
  79. ch := char(fc_GetC(fc));
  80. Until (ch = #13) or (ch = EOF);
  81. text_getc := ch;
  82. end;
  83. {LOCAL}
  84. function read_text_integer (var f : Cache;
  85. var outval : long;
  86. var termchar : char) : boolean;
  87. { Read an unsigned decimal integer from a file, store it in outval }
  88. { Reads one trailing character after the integer; returns it in termchar }
  89. var
  90. {register} ch : char;
  91. {register} val : long;
  92. begin
  93. { Skip any leading whitespace, detect EOF }
  94. repeat
  95. ch := text_getc(f);
  96. if (ch = EOF) then
  97. begin
  98. termchar := EOF;
  99. read_text_integer := FALSE;
  100. exit;
  101. end;
  102. Until (ch <> BLANK) and (ch <> TAB) and (ch <> CR) and (ch <> LF);
  103. if not (ch in ['0'..'9']) then
  104. begin
  105. termchar := ch;
  106. read_text_integer := FALSE;
  107. exit;
  108. end;
  109. val := ord(ch) - ord('0');
  110. repeat
  111. ch := text_getc(f);
  112. if (ch <> EOF) then
  113. begin
  114. if not (ch in ['0'..'9']) then
  115. break;
  116. val := val * 10;
  117. Inc(val, ord(ch) - ord('0'));
  118. end;
  119. until ch = EOF;
  120. outval := val;
  121. termchar := ch;
  122. read_text_integer := TRUE;
  123. end;
  124. {GLOBAL}
  125. function read_quant_tables (cinfo : j_compress_ptr;
  126. const filename : string;
  127. scale_factor : int;
  128. force_baseline : boolean) : boolean;
  129. { Read a set of quantization tables from the specified file.
  130. The file is plain ASCII text: decimal numbers with whitespace between.
  131. Comments preceded by '#' may be included in the file.
  132. There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
  133. The tables are implicitly numbered 0,1,etc.
  134. NOTE: does not affect the qslots mapping, which will default to selecting
  135. table 0 for luminance (or primary) components, 1 for chrominance components.
  136. You must use -qslots if you want a different component->table mapping. }
  137. var
  138. f : file;
  139. fp : Cache;
  140. tblno, i : int;
  141. termchar : char;
  142. val : long;
  143. table : array[0..DCTSIZE2-1] of uInt;
  144. begin
  145. Assign(f, filename);
  146. {$push}{$I-}
  147. Reset(f, 1);
  148. {$pop}
  149. if (IOresult <> 0) then
  150. begin
  151. WriteLn(output, 'Can''t open table file ', filename);
  152. read_quant_tables := FALSE;
  153. exit;
  154. end;
  155. fc_Init(fp, f, 0);
  156. tblno := 0;
  157. while (read_text_integer(fp, val, termchar)) do
  158. begin { read 1st element of table }
  159. if (tblno >= NUM_QUANT_TBLS) then
  160. begin
  161. WriteLn(output, 'Too many tables in file ', filename);
  162. fc_close(fp);
  163. read_quant_tables := FALSE;
  164. exit;
  165. end;
  166. table[0] := uInt (val);
  167. for i := 1 to pred(DCTSIZE2) do
  168. begin
  169. if (not read_text_integer(fp, val, termchar)) then
  170. begin
  171. WriteLn(output, 'Invalid table data in file ', filename);
  172. fc_close(fp);
  173. read_quant_tables := FALSE;
  174. exit;
  175. end;
  176. table[i] := uInt (val);
  177. end;
  178. jpeg_add_quant_table(cinfo, tblno, table, scale_factor, force_baseline);
  179. Inc(tblno);
  180. end;
  181. if (termchar <> EOF) then
  182. begin
  183. WriteLn(output, 'Non-numeric data in file ', filename);
  184. fc_close(fp);
  185. read_quant_tables := FALSE;
  186. exit;
  187. end;
  188. fc_close(fp);
  189. read_quant_tables := TRUE;
  190. end;
  191. {$ifdef C_MULTISCAN_FILES_SUPPORTED}
  192. {LOCAL}
  193. function read_scan_integer (var f : cache;
  194. var outval : long;
  195. var termchar : char) : boolean;
  196. { Variant of read_text_integer that always looks for a non-space termchar;
  197. this simplifies parsing of punctuation in scan scripts. }
  198. var
  199. ch : char; { register }
  200. begin
  201. if not read_text_integer(f, outval, termchar) then
  202. begin
  203. read_scan_integer := FALSE;
  204. exit;
  205. end;
  206. ch := termchar;
  207. while (ch <> EOF) and (ch in [BLANK, TAB]) do
  208. ch := text_getc(f);
  209. if (ch in ['0'..'9']) then
  210. begin { oops, put it back }
  211. if fc_ungetc(f, ch) = Byte(EOF) then
  212. begin
  213. read_scan_integer := FALSE;
  214. exit;
  215. end;
  216. ch := BLANK;
  217. end
  218. else
  219. begin
  220. { Any separators other than ';' and ':' are ignored;
  221. this allows user to insert commas, etc, if desired. }
  222. if (ch <> EOF) and (ch <> ';') and (ch <> ':') then
  223. ch := BLANK;
  224. end;
  225. termchar := ch;
  226. read_scan_integer := TRUE;
  227. end;
  228. {GLOBAL}
  229. function read_scan_script (cinfo : j_compress_ptr;
  230. const filename : string) : boolean;
  231. { Read a scan script from the specified text file.
  232. Each entry in the file defines one scan to be emitted.
  233. Entries are separated by semicolons ';'.
  234. An entry contains one to four component indexes,
  235. optionally followed by a colon ':' and four progressive-JPEG parameters.
  236. The component indexes denote which component(s) are to be transmitted
  237. in the current scan. The first component has index 0.
  238. Sequential JPEG is used if the progressive-JPEG parameters are omitted.
  239. The file is free format text: any whitespace may appear between numbers
  240. and the ':' and ';' punctuation marks. Also, other punctuation (such
  241. as commas or dashes) can be placed between numbers if desired.
  242. Comments preceded by '#' may be included in the file.
  243. Note: we do very little validity checking here;
  244. jcmaster.c will validate the script parameters. }
  245. label
  246. bogus;
  247. var
  248. f : file;
  249. fp : Cache;
  250. scanno, ncomps : int;
  251. termchar : char;
  252. val : long;
  253. scanptr : jpeg_scan_info_ptr;
  254. const
  255. MAX_SCANS = 100; { quite arbitrary limit }
  256. var
  257. scans : array[0..MAX_SCANS-1] of jpeg_scan_info;
  258. begin
  259. Assign(f,filename);
  260. {$push}{$I-}
  261. Reset(f, 1);
  262. {$pop}
  263. if (IOresult <> 0) then
  264. begin
  265. WriteLn('Can''t open scan definition file ', filename);
  266. read_scan_script := FALSE;
  267. exit;
  268. end;
  269. fc_Init(fp, f, 0);
  270. scanptr := @scans[0];
  271. scanno := 0;
  272. while (read_scan_integer(fp, val, termchar)) do
  273. begin
  274. if (scanno >= MAX_SCANS) then
  275. begin
  276. WriteLn(output, 'Too many scans defined in file ', filename);
  277. fc_Close(fp);
  278. read_scan_script := FALSE;
  279. exit;
  280. end;
  281. scanptr^.component_index[0] := int(val);
  282. ncomps := 1;
  283. while (termchar = BLANK) do
  284. begin
  285. if (ncomps >= MAX_COMPS_IN_SCAN) then
  286. begin
  287. WriteLn(output, 'Too many components in one scan in file ',
  288. filename);
  289. fc_close(fp);
  290. read_scan_script := FALSE;
  291. exit;
  292. end;
  293. if (not read_scan_integer(fp, val, termchar)) then
  294. goto bogus;
  295. scanptr^.component_index[ncomps] := int (val);
  296. Inc(ncomps);
  297. end;
  298. scanptr^.comps_in_scan := ncomps;
  299. if (termchar = ':') then
  300. begin
  301. if (not read_scan_integer(fp, val, termchar)) or (termchar <> BLANK) then
  302. goto bogus;
  303. scanptr^.Ss := int (val);
  304. if (not read_scan_integer(fp, val, termchar)) or (termchar <> BLANK) then
  305. goto bogus;
  306. scanptr^.Se := int (val);
  307. if (not read_scan_integer(fp, val, termchar)) or (termchar <> BLANK) then
  308. goto bogus;
  309. scanptr^.Ah := int (val);
  310. if (not read_scan_integer(fp, val, termchar)) then
  311. goto bogus;
  312. scanptr^.Al := int (val);
  313. end
  314. else
  315. begin
  316. { set non-progressive parameters }
  317. scanptr^.Ss := 0;
  318. scanptr^.Se := DCTSIZE2-1;
  319. scanptr^.Ah := 0;
  320. scanptr^.Al := 0;
  321. end;
  322. if (termchar <> ';') and (termchar <> EOF) then
  323. begin
  324. bogus:
  325. WriteLn(output, 'Invalid scan entry format in file ', filename);
  326. fc_close(fp);
  327. read_scan_script := FALSE;
  328. exit;
  329. end;
  330. Inc(scanptr);
  331. Inc(scanno);
  332. end;
  333. if (termchar <> EOF) then
  334. begin
  335. WriteLn(output, 'Non-numeric data in file ', filename);
  336. fc_close(fp);
  337. read_scan_script := FALSE;
  338. exit;
  339. end;
  340. if (scanno > 0) then
  341. begin
  342. { Stash completed scan list in cinfo structure.
  343. NOTE: for cjpeg's use, JPOOL_IMAGE is the right lifetime for this data,
  344. but if you want to compress multiple images you'd want JPOOL_PERMANENT. }
  345. scanptr := jpeg_scan_info_ptr (
  346. cinfo^.mem^.alloc_small ( j_common_ptr(cinfo), JPOOL_IMAGE,
  347. scanno * SIZEOF(jpeg_scan_info)) );
  348. MEMCOPY(scanptr, @scans, scanno * SIZEOF(jpeg_scan_info));
  349. cinfo^.scan_info := scanptr;
  350. cinfo^.num_scans := scanno;
  351. end;
  352. fc_close(fp);
  353. read_scan_script := TRUE;
  354. end;
  355. {$endif} { C_MULTISCAN_FILES_SUPPORTED }
  356. function sscanf(var lineptr : PChar;
  357. var val : int;
  358. var ch : char) : boolean;
  359. var
  360. digits : int;
  361. begin
  362. digits := 0;
  363. while (lineptr^=BLANK) do { advance to next segment of the string }
  364. Inc(lineptr);
  365. val := 0;
  366. while lineptr^ in ['0'..'9'] do
  367. begin
  368. val := val * 10 + (ord(lineptr^) - ord('0'));
  369. Inc(lineptr);
  370. Inc(digits);
  371. end;
  372. if lineptr^<>#0 then
  373. begin
  374. ch := lineptr^;
  375. Inc(lineptr);
  376. end;
  377. sscanf := (digits > 0);
  378. end;
  379. {GLOBAL}
  380. function set_quant_slots (cinfo : j_compress_ptr;
  381. argtxt : string) : boolean;
  382. { Process a quantization-table-selectors parameter string, of the form
  383. N[,N,...]
  384. If there are more components than parameters, the last value is replicated.
  385. }
  386. var
  387. val : int; { default table # }
  388. ci : int;
  389. ch : char;
  390. var
  391. arg_copy : string;
  392. arg : PChar;
  393. begin
  394. arg_copy := argtxt + #0;
  395. if arg_copy[Length(arg_copy)] <> #0 then
  396. arg_copy[Length(arg_copy)] := #0;
  397. arg := @arg_copy[1];
  398. val := 0;
  399. for ci := 0 to pred(MAX_COMPONENTS) do
  400. begin
  401. if (arg^ <> #0) then
  402. begin
  403. ch := ','; { if not set by sscanf, will be ',' }
  404. if not sscanf(arg, val, ch) then
  405. begin
  406. set_quant_slots := FALSE;
  407. exit;
  408. end;
  409. if (ch <> ',') then { syntax check }
  410. begin
  411. set_quant_slots := FALSE;
  412. exit;
  413. end;
  414. if (val < 0) or (val >= NUM_QUANT_TBLS) then
  415. begin
  416. WriteLn(output, 'JPEG quantization tables are numbered 0..',
  417. NUM_QUANT_TBLS-1);
  418. set_quant_slots := FALSE;
  419. exit;
  420. end;
  421. cinfo^.comp_info^[ci].quant_tbl_no := val;
  422. end
  423. else
  424. begin
  425. { reached end of parameter, set remaining components to last table }
  426. cinfo^.comp_info^[ci].quant_tbl_no := val;
  427. end;
  428. end;
  429. set_quant_slots := TRUE;
  430. end;
  431. {GLOBAL}
  432. function set_sample_factors (cinfo : j_compress_ptr;
  433. argtxt : string) : boolean;
  434. { Process a sample-factors parameter string, of the form
  435. HxV[,HxV,...]
  436. If there are more components than parameters, "1x1" is assumed for the rest.
  437. }
  438. var
  439. ci, val1, val2 : int;
  440. ch1, ch2 : char;
  441. var
  442. arg_copy : string;
  443. arg : PChar;
  444. begin
  445. arg_copy := argtxt + #0;
  446. if arg_copy[Length(arg_copy)] <> #0 then
  447. arg_copy[Length(arg_copy)] := #0;
  448. arg := @arg_copy[1];
  449. for ci := 0 to pred(MAX_COMPONENTS) do
  450. begin
  451. if (arg^ <> #0) then
  452. begin
  453. ch2 := ','; { if not set by sscanf, will be ',' }
  454. if not (sscanf(arg, val1, ch1) and
  455. sscanf(arg, val2, ch2)) then
  456. begin
  457. set_sample_factors := FALSE;
  458. exit;
  459. end;
  460. if ((ch1 <> 'x') and (ch1 <> 'X')) or (ch2 <> ',') then { syntax check }
  461. begin
  462. set_sample_factors := FALSE;
  463. exit;
  464. end;
  465. if (val1 <= 0) or (val1 > 4) or (val2 <= 0) or (val2 > 4) then
  466. begin
  467. WriteLn(output, 'JPEG sampling factors must be 1..4');
  468. set_sample_factors := FALSE;
  469. exit;
  470. end;
  471. cinfo^.comp_info^[ci].h_samp_factor := val1;
  472. cinfo^.comp_info^[ci].v_samp_factor := val2;
  473. end
  474. else
  475. begin
  476. { reached end of parameter, set remaining components to 1x1 sampling }
  477. cinfo^.comp_info^[ci].h_samp_factor := 1;
  478. cinfo^.comp_info^[ci].v_samp_factor := 1;
  479. end;
  480. end;
  481. set_sample_factors := TRUE;
  482. end;
  483. end.