jpgicc.c 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  1. //---------------------------------------------------------------------------------
  2. //
  3. // Little Color Management System
  4. // Copyright (c) 1998-2023 Marti Maria Saguer
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining
  7. // a copy of this software and associated documentation files (the "Software"),
  8. // to deal in the Software without restriction, including without limitation
  9. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  10. // and/or sell copies of the Software, and to permit persons to whom the Software
  11. // is furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  18. // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. //
  24. // This program does apply profiles to (some) JPEG files
  25. #include "utils.h"
  26. #include "jpeglib.h"
  27. #include "iccjpeg.h"
  28. // Flags
  29. static cmsBool BlackPointCompensation = FALSE;
  30. static cmsBool IgnoreEmbedded = FALSE;
  31. static cmsBool GamutCheck = FALSE;
  32. static cmsBool lIsITUFax = FALSE;
  33. static cmsBool lIsPhotoshopApp13 = FALSE;
  34. static cmsBool lIsEXIF;
  35. static cmsBool lIsDeviceLink = FALSE;
  36. static cmsBool EmbedProfile = FALSE;
  37. static const char* SaveEmbedded = NULL;
  38. static int Intent = INTENT_PERCEPTUAL;
  39. static int ProofingIntent = INTENT_PERCEPTUAL;
  40. static int PrecalcMode = 1;
  41. static int jpegQuality = 75;
  42. static cmsFloat64Number ObserverAdaptationState = 0;
  43. static char *cInpProf = NULL;
  44. static char *cOutProf = NULL;
  45. static char *cProofing = NULL;
  46. static FILE * InFile;
  47. static FILE * OutFile;
  48. static struct jpeg_decompress_struct Decompressor;
  49. static struct jpeg_compress_struct Compressor;
  50. static struct my_error_mgr {
  51. struct jpeg_error_mgr pub; // "public" fields
  52. void* Cargo; // "private" fields
  53. } ErrorHandler;
  54. cmsUInt16Number Alarm[cmsMAXCHANNELS] = {128,128,128,0};
  55. static
  56. void my_error_exit (j_common_ptr cinfo)
  57. {
  58. char buffer[JMSG_LENGTH_MAX];
  59. (*cinfo->err->format_message) (cinfo, buffer);
  60. FatalError(buffer);
  61. }
  62. /*
  63. Definition of the APPn Markers Defined for continuous-tone G3FAX
  64. The application code APP1 initiates identification of the image as
  65. a G3FAX application and defines the spatial resolution and subsampling.
  66. This marker directly follows the SOI marker. The data format will be as follows:
  67. X'FFE1' (APP1), length, FAX identifier, version, spatial resolution.
  68. The above terms are defined as follows:
  69. Length: (Two octets) Total APP1 field octet count including the octet count itself, but excluding the APP1
  70. marker.
  71. FAX identifier: (Six octets) X'47', X'33', X'46', X'41', X'58', X'00'. This X'00'-terminated string "G3FAX"
  72. uniquely identifies this APP1 marker.
  73. Version: (Two octets) X'07CA'. This string specifies the year of approval of the standard, for identification
  74. in the case of future revision (for example, 1994).
  75. Spatial Resolution: (Two octets) Lightness pixel density in pels/25.4 mm. The basic value is 200. Allowed values are
  76. 100, 200, 300, 400, 600 and 1200 pels/25.4 mm, with square (or equivalent) pels.
  77. NOTE - The functional equivalence of inch-based and mm-based resolutions is maintained. For example, the 200 x 200
  78. */
  79. static
  80. cmsBool IsITUFax(jpeg_saved_marker_ptr ptr)
  81. {
  82. while (ptr)
  83. {
  84. if (ptr -> marker == (JPEG_APP0 + 1) && ptr -> data_length > 5) {
  85. const char* data = (const char*) ptr -> data;
  86. if (strcmp(data, "G3FAX") == 0) return TRUE;
  87. }
  88. ptr = ptr -> next;
  89. }
  90. return FALSE;
  91. }
  92. // Save a ITU T.42/Fax marker with defaults on boundaries. This is the only mode we support right now.
  93. static
  94. void SetITUFax(j_compress_ptr cinfo)
  95. {
  96. unsigned char Marker[] = "G3FAX\x00\0x07\xCA\x00\xC8";
  97. jpeg_write_marker(cinfo, (JPEG_APP0 + 1), Marker, 10);
  98. }
  99. // Build a profile for decoding ITU T.42/Fax JPEG streams.
  100. // The profile has an additional ability in the input direction of
  101. // gamut compress values between 85 < a < -85 and -75 < b < 125. This conforms
  102. // the default range for ITU/T.42 -- See RFC 2301, section 6.2.3 for details
  103. // L* = [0, 100]
  104. // a* = [-85, 85]
  105. // b* = [-75, 125]
  106. // These functions does convert the encoding of ITUFAX to floating point
  107. // and vice-versa. No gamut mapping is performed yet.
  108. static
  109. void ITU2Lab(const cmsUInt16Number In[3], cmsCIELab* Lab)
  110. {
  111. Lab -> L = (double) In[0] / 655.35;
  112. Lab -> a = (double) 170.* (In[1] - 32768.) / 65535.;
  113. Lab -> b = (double) 200.* (In[2] - 24576.) / 65535.;
  114. }
  115. static
  116. void Lab2ITU(const cmsCIELab* Lab, cmsUInt16Number Out[3])
  117. {
  118. Out[0] = (cmsUInt16Number) floor((double) (Lab -> L / 100.)* 65535. );
  119. Out[1] = (cmsUInt16Number) floor((double) (Lab -> a / 170.)* 65535. + 32768. );
  120. Out[2] = (cmsUInt16Number) floor((double) (Lab -> b / 200.)* 65535. + 24576. );
  121. }
  122. // These are the samplers-- They are passed as callbacks to cmsStageSampleCLut16bit()
  123. // then, cmsSample3DGrid() will sweel whole Lab gamut calling these functions
  124. // once for each node. In[] will contain the Lab PCS value to convert to ITUFAX
  125. // on PCS2ITU, or the ITUFAX value to convert to Lab in ITU2PCS
  126. // You can change the number of sample points if desired, the algorithm will
  127. // remain same. 33 points gives good accuracy, but you can reduce to 22 or less
  128. // is space is critical
  129. #define GRID_POINTS 33
  130. static
  131. int PCS2ITU(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
  132. {
  133. cmsCIELab Lab;
  134. cmsLabEncoded2Float(&Lab, In);
  135. cmsDesaturateLab(&Lab, 85, -85, 125, -75); // This function does the necessary gamut remapping
  136. Lab2ITU(&Lab, Out);
  137. return TRUE;
  138. UTILS_UNUSED_PARAMETER(Cargo);
  139. }
  140. static
  141. int ITU2PCS( register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
  142. {
  143. cmsCIELab Lab;
  144. ITU2Lab(In, &Lab);
  145. cmsFloat2LabEncoded(Out, &Lab);
  146. return TRUE;
  147. UTILS_UNUSED_PARAMETER(Cargo);
  148. }
  149. // This function does create the virtual input profile, which decodes ITU to the profile connection space
  150. static
  151. cmsHPROFILE CreateITU2PCS_ICC(void)
  152. {
  153. cmsHPROFILE hProfile;
  154. cmsPipeline* AToB0;
  155. cmsStage* ColorMap;
  156. AToB0 = cmsPipelineAlloc(0, 3, 3);
  157. if (AToB0 == NULL) return NULL;
  158. ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);
  159. if (ColorMap == NULL) return NULL;
  160. cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, ColorMap);
  161. cmsStageSampleCLut16bit(ColorMap, ITU2PCS, NULL, 0);
  162. hProfile = cmsCreateProfilePlaceholder(0);
  163. if (hProfile == NULL) {
  164. cmsPipelineFree(AToB0);
  165. return NULL;
  166. }
  167. cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB0);
  168. cmsSetColorSpace(hProfile, cmsSigLabData);
  169. cmsSetPCS(hProfile, cmsSigLabData);
  170. cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);
  171. cmsPipelineFree(AToB0);
  172. return hProfile;
  173. }
  174. // This function does create the virtual output profile, with the necessary gamut mapping
  175. static
  176. cmsHPROFILE CreatePCS2ITU_ICC(void)
  177. {
  178. cmsHPROFILE hProfile;
  179. cmsPipeline* BToA0;
  180. cmsStage* ColorMap;
  181. BToA0 = cmsPipelineAlloc(0, 3, 3);
  182. if (BToA0 == NULL) return NULL;
  183. ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);
  184. if (ColorMap == NULL) return NULL;
  185. cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, ColorMap);
  186. cmsStageSampleCLut16bit(ColorMap, PCS2ITU, NULL, 0);
  187. hProfile = cmsCreateProfilePlaceholder(0);
  188. if (hProfile == NULL) {
  189. cmsPipelineFree(BToA0);
  190. return NULL;
  191. }
  192. cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA0);
  193. cmsSetColorSpace(hProfile, cmsSigLabData);
  194. cmsSetPCS(hProfile, cmsSigLabData);
  195. cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);
  196. cmsPipelineFree(BToA0);
  197. return hProfile;
  198. }
  199. #define PS_FIXED_TO_FLOAT(h, l) ((float) (h) + ((float) (l)/(1<<16)))
  200. static
  201. cmsBool ProcessPhotoshopAPP13(JOCTET *data, int datalen)
  202. {
  203. int i;
  204. for (i = 14; i < datalen; )
  205. {
  206. long len;
  207. unsigned int type;
  208. if (!(GETJOCTET(data[i] ) == 0x38 &&
  209. GETJOCTET(data[i+1]) == 0x42 &&
  210. GETJOCTET(data[i+2]) == 0x49 &&
  211. GETJOCTET(data[i+3]) == 0x4D)) break; // Not recognized
  212. i += 4; // identifying string
  213. type = (unsigned int) (GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]));
  214. i += 2; // resource type
  215. i += GETJOCTET(data[i]) + ((GETJOCTET(data[i]) & 1) ? 1 : 2); // resource name
  216. len = ((((GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]))<<8) +
  217. GETJOCTET(data[i+2]))<<8) + GETJOCTET(data[i+3]);
  218. if (len < 0) return FALSE; // Keep bug hunters away
  219. i += 4; // Size
  220. if (type == 0x03ED && len >= 16) {
  221. Decompressor.X_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]),
  222. GETJOCTET(data[i+2]<<8) + GETJOCTET(data[i+3]));
  223. Decompressor.Y_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i+8]<<8) + GETJOCTET(data[i+9]),
  224. GETJOCTET(data[i+10]<<8) + GETJOCTET(data[i+11]));
  225. // Set the density unit to 1 since the
  226. // Vertical and Horizontal resolutions
  227. // are specified in Pixels per inch
  228. Decompressor.density_unit = 0x01;
  229. return TRUE;
  230. }
  231. i += len + ((len & 1) ? 1 : 0); // Alignment
  232. }
  233. return FALSE;
  234. }
  235. static
  236. cmsBool HandlePhotoshopAPP13(jpeg_saved_marker_ptr ptr)
  237. {
  238. while (ptr) {
  239. if (ptr -> marker == (JPEG_APP0 + 13) && ptr -> data_length > 9)
  240. {
  241. JOCTET* data = ptr -> data;
  242. if(GETJOCTET(data[0]) == 0x50 &&
  243. GETJOCTET(data[1]) == 0x68 &&
  244. GETJOCTET(data[2]) == 0x6F &&
  245. GETJOCTET(data[3]) == 0x74 &&
  246. GETJOCTET(data[4]) == 0x6F &&
  247. GETJOCTET(data[5]) == 0x73 &&
  248. GETJOCTET(data[6]) == 0x68 &&
  249. GETJOCTET(data[7]) == 0x6F &&
  250. GETJOCTET(data[8]) == 0x70) {
  251. ProcessPhotoshopAPP13(data, ptr -> data_length);
  252. return TRUE;
  253. }
  254. }
  255. ptr = ptr -> next;
  256. }
  257. return FALSE;
  258. }
  259. typedef unsigned short uint16_t;
  260. typedef unsigned char uint8_t;
  261. typedef unsigned int uint32_t;
  262. #define INTEL_BYTE_ORDER 0x4949
  263. #define XRESOLUTION 0x011a
  264. #define YRESOLUTION 0x011b
  265. #define RESOLUTION_UNIT 0x128
  266. // Abort if crafted file
  267. static
  268. void craftedFile(void)
  269. {
  270. FatalError("Corrupted EXIF data");
  271. }
  272. // Read a 16-bit word
  273. static
  274. uint16_t read16(uint8_t* arr, size_t pos, int swapBytes, size_t max)
  275. {
  276. if (pos + 2 >= max)
  277. {
  278. craftedFile();
  279. return 0;
  280. }
  281. else
  282. {
  283. uint8_t b1 = arr[pos];
  284. uint8_t b2 = arr[pos + 1];
  285. return (swapBytes) ? ((b2 << 8) | b1) : ((b1 << 8) | b2);
  286. }
  287. }
  288. // Read a 32-bit word
  289. static
  290. uint32_t read32(uint8_t* arr, size_t pos, int swapBytes, size_t max)
  291. {
  292. if (pos + 4 >= max)
  293. {
  294. craftedFile();
  295. return 0;
  296. }
  297. else
  298. {
  299. if (!swapBytes) {
  300. return (arr[pos] << 24) | (arr[pos + 1] << 16) | (arr[pos + 2] << 8) | arr[pos + 3];
  301. }
  302. return arr[pos] | (arr[pos + 1] << 8) | (arr[pos + 2] << 16) | (arr[pos + 3] << 24);
  303. }
  304. }
  305. static
  306. int read_tag(uint8_t* arr, int pos, int swapBytes, void* dest, size_t max)
  307. {
  308. // Format should be 5 over here (rational)
  309. uint32_t format = read16(arr, pos + 2, swapBytes, max);
  310. // Components should be 1
  311. uint32_t components = read32(arr, pos + 4, swapBytes, max);
  312. // Points to the value
  313. uint32_t offset;
  314. // sanity
  315. if (components != 1) return 0;
  316. if (format == 3)
  317. offset = pos + 8;
  318. else
  319. offset = read32(arr, pos + 8, swapBytes, max);
  320. switch (format) {
  321. case 5: // Rational
  322. {
  323. double num = read32(arr, offset, swapBytes, max);
  324. double den = read32(arr, offset + 4, swapBytes, max);
  325. *(double *) dest = num / den;
  326. }
  327. break;
  328. case 3: // uint 16
  329. *(int*) dest = read16(arr, offset, swapBytes, max);
  330. break;
  331. default: return 0;
  332. }
  333. return 1;
  334. }
  335. // Handler for EXIF data
  336. static
  337. cmsBool HandleEXIF(struct jpeg_decompress_struct* cinfo)
  338. {
  339. jpeg_saved_marker_ptr ptr;
  340. uint32_t ifd_ofs;
  341. int pos = 0, swapBytes = 0;
  342. uint32_t i, numEntries;
  343. double XRes = -1, YRes = -1;
  344. int Unit = 2; // Inches
  345. for (ptr = cinfo ->marker_list; ptr; ptr = ptr ->next) {
  346. if ((ptr ->marker == JPEG_APP0+1) && ptr ->data_length > 6) {
  347. JOCTET* data = ptr -> data;
  348. size_t max = ptr->data_length;
  349. if (memcmp(data, "Exif\0\0", 6) == 0) {
  350. data += 6; // Skip EXIF marker
  351. // 8 byte TIFF header
  352. // first two determine byte order
  353. pos = 0;
  354. if (read16(data, pos, 0, max) == INTEL_BYTE_ORDER) {
  355. swapBytes = 1;
  356. }
  357. pos += 2;
  358. // next two bytes are always 0x002A (TIFF version)
  359. pos += 2;
  360. // offset to Image File Directory (includes the previous 8 bytes)
  361. ifd_ofs = read32(data, pos, swapBytes, max);
  362. // Search the directory for resolution tags
  363. numEntries = read16(data, ifd_ofs, swapBytes, max);
  364. for (i=0; i < numEntries; i++) {
  365. uint32_t entryOffset = ifd_ofs + 2 + (12 * i);
  366. uint32_t tag = read16(data, entryOffset, swapBytes, max);
  367. switch (tag) {
  368. case RESOLUTION_UNIT:
  369. if (!read_tag(data, entryOffset, swapBytes, &Unit, max)) return FALSE;
  370. break;
  371. case XRESOLUTION:
  372. if (!read_tag(data, entryOffset, swapBytes, &XRes, max)) return FALSE;
  373. break;
  374. case YRESOLUTION:
  375. if (!read_tag(data, entryOffset, swapBytes, &YRes, max)) return FALSE;
  376. break;
  377. default:;
  378. }
  379. }
  380. // Proceed if all found
  381. if (XRes != -1 && YRes != -1)
  382. {
  383. // 1 = None
  384. // 2 = inches
  385. // 3 = cm
  386. switch (Unit) {
  387. case 2:
  388. cinfo ->X_density = (UINT16) floor(XRes + 0.5);
  389. cinfo ->Y_density = (UINT16) floor(YRes + 0.5);
  390. break;
  391. case 1:
  392. cinfo ->X_density = (UINT16) floor(XRes * 2.54 + 0.5);
  393. cinfo ->Y_density = (UINT16) floor(YRes * 2.54 + 0.5);
  394. break;
  395. default: return FALSE;
  396. }
  397. cinfo ->density_unit = 1; /* 1 for dots/inch, or 2 for dots/cm.*/
  398. }
  399. }
  400. }
  401. }
  402. return FALSE;
  403. }
  404. static
  405. cmsBool OpenInput(const char* FileName)
  406. {
  407. int m;
  408. lIsITUFax = FALSE;
  409. InFile = fopen(FileName, "rb");
  410. if (InFile == NULL) {
  411. FatalError("Cannot open '%s'", FileName);
  412. }
  413. // Now we can initialize the JPEG decompression object.
  414. Decompressor.err = jpeg_std_error(&ErrorHandler.pub);
  415. ErrorHandler.pub.error_exit = my_error_exit;
  416. ErrorHandler.pub.output_message = my_error_exit;
  417. jpeg_create_decompress(&Decompressor);
  418. jpeg_stdio_src(&Decompressor, InFile);
  419. for (m = 0; m < 16; m++)
  420. jpeg_save_markers(&Decompressor, JPEG_APP0 + m, 0xFFFF);
  421. // setup_read_icc_profile(&Decompressor);
  422. fseek(InFile, 0, SEEK_SET);
  423. jpeg_read_header(&Decompressor, TRUE);
  424. return TRUE;
  425. }
  426. static
  427. cmsBool OpenOutput(const char* FileName)
  428. {
  429. OutFile = fopen(FileName, "wb");
  430. if (OutFile == NULL) {
  431. FatalError("Cannot create '%s'", FileName);
  432. }
  433. Compressor.err = jpeg_std_error(&ErrorHandler.pub);
  434. ErrorHandler.pub.error_exit = my_error_exit;
  435. ErrorHandler.pub.output_message = my_error_exit;
  436. Compressor.input_components = Compressor.num_components = 4;
  437. jpeg_create_compress(&Compressor);
  438. jpeg_stdio_dest(&Compressor, OutFile);
  439. return TRUE;
  440. }
  441. static
  442. cmsBool Done(void)
  443. {
  444. jpeg_destroy_decompress(&Decompressor);
  445. jpeg_destroy_compress(&Compressor);
  446. return fclose(InFile) + fclose(OutFile);
  447. }
  448. // Build up the pixeltype descriptor
  449. static
  450. cmsUInt32Number GetInputPixelType(void)
  451. {
  452. int space, bps, extra, ColorChannels, Flavor;
  453. lIsITUFax = IsITUFax(Decompressor.marker_list);
  454. lIsPhotoshopApp13 = HandlePhotoshopAPP13(Decompressor.marker_list);
  455. lIsEXIF = HandleEXIF(&Decompressor);
  456. ColorChannels = Decompressor.num_components;
  457. extra = 0; // Alpha = None
  458. bps = 1; // 8 bits
  459. Flavor = 0; // Vanilla
  460. if (lIsITUFax) {
  461. space = PT_Lab;
  462. Decompressor.out_color_space = JCS_YCbCr; // Fake to don't touch
  463. }
  464. else
  465. switch (Decompressor.jpeg_color_space) {
  466. case JCS_GRAYSCALE: // monochrome
  467. space = PT_GRAY;
  468. Decompressor.out_color_space = JCS_GRAYSCALE;
  469. break;
  470. case JCS_RGB: // red/green/blue
  471. space = PT_RGB;
  472. Decompressor.out_color_space = JCS_RGB;
  473. break;
  474. case JCS_YCbCr: // Y/Cb/Cr (also known as YUV)
  475. space = PT_RGB; // Let IJG code to do the conversion
  476. Decompressor.out_color_space = JCS_RGB;
  477. break;
  478. case JCS_CMYK: // C/M/Y/K
  479. space = PT_CMYK;
  480. Decompressor.out_color_space = JCS_CMYK;
  481. if (Decompressor.saw_Adobe_marker) // Adobe keeps CMYK inverted, so change flavor
  482. Flavor = 1; // from vanilla to chocolate
  483. break;
  484. case JCS_YCCK: // Y/Cb/Cr/K
  485. space = PT_CMYK;
  486. Decompressor.out_color_space = JCS_CMYK;
  487. if (Decompressor.saw_Adobe_marker) // ditto
  488. Flavor = 1;
  489. break;
  490. default:
  491. FatalError("Unsupported color space (0x%x)", Decompressor.jpeg_color_space);
  492. return 0;
  493. }
  494. return (EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|COLORSPACE_SH(space)|FLAVOR_SH(Flavor));
  495. }
  496. // Rearrange pixel type to build output descriptor
  497. static
  498. cmsUInt32Number ComputeOutputFormatDescriptor(cmsUInt32Number dwInput, int OutColorSpace)
  499. {
  500. int IsPlanar = T_PLANAR(dwInput);
  501. int Channels = 0;
  502. int Flavor = 0;
  503. switch (OutColorSpace) {
  504. case PT_GRAY:
  505. Channels = 1;
  506. break;
  507. case PT_RGB:
  508. case PT_CMY:
  509. case PT_Lab:
  510. case PT_YUV:
  511. case PT_YCbCr:
  512. Channels = 3;
  513. break;
  514. case PT_CMYK:
  515. if (Compressor.write_Adobe_marker) // Adobe keeps CMYK inverted, so change flavor to chocolate
  516. Flavor = 1;
  517. Channels = 4;
  518. break;
  519. default:
  520. FatalError("Unsupported output color space");
  521. }
  522. return (COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(1)|FLAVOR_SH(Flavor));
  523. }
  524. // Equivalence between ICC color spaces and lcms color spaces
  525. static
  526. int GetProfileColorSpace(cmsHPROFILE hProfile)
  527. {
  528. cmsColorSpaceSignature ProfileSpace = cmsGetColorSpace(hProfile);
  529. return _cmsLCMScolorSpace(ProfileSpace);
  530. }
  531. static
  532. int GetDevicelinkColorSpace(cmsHPROFILE hProfile)
  533. {
  534. cmsColorSpaceSignature ProfileSpace = cmsGetPCS(hProfile);
  535. return _cmsLCMScolorSpace(ProfileSpace);
  536. }
  537. // From TRANSUPP
  538. static
  539. void jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dstinfo)
  540. {
  541. jpeg_saved_marker_ptr marker;
  542. /* In the current implementation, we don't actually need to examine the
  543. * option flag here; we just copy everything that got saved.
  544. * But to avoid confusion, we do not output JFIF and Adobe APP14 markers
  545. * if the encoder library already wrote one.
  546. */
  547. for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {
  548. if (dstinfo->write_JFIF_header &&
  549. marker->marker == JPEG_APP0 &&
  550. marker->data_length >= 5 &&
  551. GETJOCTET(marker->data[0]) == 0x4A &&
  552. GETJOCTET(marker->data[1]) == 0x46 &&
  553. GETJOCTET(marker->data[2]) == 0x49 &&
  554. GETJOCTET(marker->data[3]) == 0x46 &&
  555. GETJOCTET(marker->data[4]) == 0)
  556. continue; /* reject duplicate JFIF */
  557. if (dstinfo->write_Adobe_marker &&
  558. marker->marker == JPEG_APP0+14 &&
  559. marker->data_length >= 5 &&
  560. GETJOCTET(marker->data[0]) == 0x41 &&
  561. GETJOCTET(marker->data[1]) == 0x64 &&
  562. GETJOCTET(marker->data[2]) == 0x6F &&
  563. GETJOCTET(marker->data[3]) == 0x62 &&
  564. GETJOCTET(marker->data[4]) == 0x65)
  565. continue; /* reject duplicate Adobe */
  566. jpeg_write_marker(dstinfo, marker->marker,
  567. marker->data, marker->data_length);
  568. }
  569. }
  570. static
  571. void WriteOutputFields(int OutputColorSpace)
  572. {
  573. J_COLOR_SPACE in_space, jpeg_space;
  574. int components;
  575. switch (OutputColorSpace) {
  576. case PT_GRAY: in_space = jpeg_space = JCS_GRAYSCALE;
  577. components = 1;
  578. break;
  579. case PT_RGB: in_space = JCS_RGB;
  580. jpeg_space = JCS_YCbCr;
  581. components = 3;
  582. break; // red/green/blue
  583. case PT_YCbCr: in_space = jpeg_space = JCS_YCbCr;
  584. components = 3;
  585. break; // Y/Cb/Cr (also known as YUV)
  586. case PT_CMYK: in_space = JCS_CMYK;
  587. jpeg_space = JCS_YCCK;
  588. components = 4;
  589. break; // C/M/Y/components
  590. case PT_Lab: in_space = jpeg_space = JCS_YCbCr;
  591. components = 3;
  592. break; // Fake to don't touch
  593. default:
  594. FatalError("Unsupported output color space");
  595. return;
  596. }
  597. if (jpegQuality >= 100) {
  598. // avoid destructive conversion when asking for lossless compression
  599. jpeg_space = in_space;
  600. }
  601. Compressor.in_color_space = in_space;
  602. Compressor.jpeg_color_space = jpeg_space;
  603. Compressor.input_components = Compressor.num_components = components;
  604. jpeg_set_defaults(&Compressor);
  605. jpeg_set_colorspace(&Compressor, jpeg_space);
  606. // Make sure to pass resolution through
  607. if (OutputColorSpace == PT_CMYK)
  608. Compressor.write_JFIF_header = 1;
  609. // Avoid subsampling on high quality factor
  610. jpeg_set_quality(&Compressor, jpegQuality, 1);
  611. if (jpegQuality >= 70) {
  612. int i;
  613. for(i=0; i < Compressor.num_components; i++) {
  614. Compressor.comp_info[i].h_samp_factor = 1;
  615. Compressor.comp_info[i].v_samp_factor = 1;
  616. }
  617. }
  618. }
  619. static
  620. void DoEmbedProfile(const char* ProfileFile)
  621. {
  622. FILE* f;
  623. size_t size, EmbedLen;
  624. cmsUInt8Number* EmbedBuffer;
  625. f = fopen(ProfileFile, "rb");
  626. if (f == NULL) return;
  627. size = cmsfilelength(f);
  628. EmbedBuffer = (cmsUInt8Number*) malloc(size + 1);
  629. EmbedLen = fread(EmbedBuffer, 1, size, f);
  630. fclose(f);
  631. EmbedBuffer[EmbedLen] = 0;
  632. write_icc_profile (&Compressor, EmbedBuffer, (unsigned int) EmbedLen);
  633. free(EmbedBuffer);
  634. }
  635. static
  636. int DoTransform(cmsHTRANSFORM hXForm, int OutputColorSpace)
  637. {
  638. JSAMPROW ScanLineIn;
  639. JSAMPROW ScanLineOut;
  640. //Preserve resolution values from the original
  641. // (Thanks to Robert Bergs for finding out this bug)
  642. Compressor.density_unit = Decompressor.density_unit;
  643. Compressor.X_density = Decompressor.X_density;
  644. Compressor.Y_density = Decompressor.Y_density;
  645. // Compressor.write_JFIF_header = 1;
  646. jpeg_start_decompress(&Decompressor);
  647. jpeg_start_compress(&Compressor, TRUE);
  648. if (OutputColorSpace == PT_Lab)
  649. SetITUFax(&Compressor);
  650. // Embed the profile if needed
  651. if (EmbedProfile && cOutProf)
  652. DoEmbedProfile(cOutProf);
  653. ScanLineIn = (JSAMPROW) malloc((size_t) Decompressor.output_width * Decompressor.num_components);
  654. ScanLineOut = (JSAMPROW) malloc((size_t) Compressor.image_width * Compressor.num_components);
  655. while (Decompressor.output_scanline <
  656. Decompressor.output_height) {
  657. jpeg_read_scanlines(&Decompressor, &ScanLineIn, 1);
  658. cmsDoTransform(hXForm, ScanLineIn, ScanLineOut, Decompressor.output_width);
  659. jpeg_write_scanlines(&Compressor, &ScanLineOut, 1);
  660. }
  661. free(ScanLineIn);
  662. free(ScanLineOut);
  663. jpeg_finish_decompress(&Decompressor);
  664. jpeg_finish_compress(&Compressor);
  665. return TRUE;
  666. }
  667. // Transform one image
  668. static
  669. int TransformImage(char *cDefInpProf, char *cOutputProf)
  670. {
  671. cmsHPROFILE hIn, hOut, hProof;
  672. cmsHTRANSFORM xform;
  673. cmsUInt32Number wInput, wOutput;
  674. int OutputColorSpace;
  675. cmsUInt32Number dwFlags = 0;
  676. cmsUInt32Number EmbedLen;
  677. cmsUInt8Number* EmbedBuffer;
  678. cmsSetAdaptationState(ObserverAdaptationState);
  679. if (BlackPointCompensation) {
  680. dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
  681. }
  682. switch (PrecalcMode) {
  683. case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;
  684. case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
  685. case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
  686. default:;
  687. }
  688. if (GamutCheck) {
  689. dwFlags |= cmsFLAGS_GAMUTCHECK;
  690. cmsSetAlarmCodes(Alarm);
  691. }
  692. // Take input color space
  693. wInput = GetInputPixelType();
  694. if (lIsDeviceLink) {
  695. hIn = cmsOpenProfileFromFile(cDefInpProf, "r");
  696. hOut = NULL;
  697. hProof = NULL;
  698. }
  699. else {
  700. if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen))
  701. {
  702. hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
  703. if (Verbose) {
  704. fprintf(stdout, " (Embedded profile found)\n");
  705. PrintProfileInformation(hIn);
  706. fflush(stdout);
  707. }
  708. if (hIn != NULL && SaveEmbedded != NULL)
  709. SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded);
  710. free(EmbedBuffer);
  711. }
  712. else
  713. {
  714. // Default for ITU/Fax
  715. if (cDefInpProf == NULL && T_COLORSPACE(wInput) == PT_Lab)
  716. cDefInpProf = "*Lab";
  717. if (cDefInpProf != NULL && cmsstrcasecmp(cDefInpProf, "*lab") == 0)
  718. hIn = CreateITU2PCS_ICC();
  719. else
  720. hIn = OpenStockProfile(0, cDefInpProf);
  721. }
  722. if (cOutputProf != NULL && cmsstrcasecmp(cOutputProf, "*lab") == 0)
  723. hOut = CreatePCS2ITU_ICC();
  724. else
  725. hOut = OpenStockProfile(0, cOutputProf);
  726. hProof = NULL;
  727. if (cProofing != NULL) {
  728. hProof = OpenStockProfile(0, cProofing);
  729. if (hProof == NULL) {
  730. FatalError("Proofing profile couldn't be read.");
  731. }
  732. dwFlags |= cmsFLAGS_SOFTPROOFING;
  733. }
  734. }
  735. if (!hIn)
  736. FatalError("Input profile couldn't be read.");
  737. if (!lIsDeviceLink && !hOut)
  738. FatalError("Output profile couldn't be read.");
  739. // Assure both, input profile and input JPEG are on same colorspace
  740. if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput)))
  741. FatalError("Input profile is not operating in proper color space");
  742. // Output colorspace is given by output profile
  743. if (lIsDeviceLink) {
  744. OutputColorSpace = GetDevicelinkColorSpace(hIn);
  745. }
  746. else {
  747. OutputColorSpace = GetProfileColorSpace(hOut);
  748. }
  749. jpeg_copy_critical_parameters(&Decompressor, &Compressor);
  750. WriteOutputFields(OutputColorSpace);
  751. wOutput = ComputeOutputFormatDescriptor(wInput, OutputColorSpace);
  752. xform = cmsCreateProofingTransform(hIn, wInput,
  753. hOut, wOutput,
  754. hProof, Intent,
  755. ProofingIntent, dwFlags);
  756. if (xform == NULL)
  757. FatalError("Cannot transform by using the profiles");
  758. DoTransform(xform, OutputColorSpace);
  759. jcopy_markers_execute(&Decompressor, &Compressor);
  760. cmsDeleteTransform(xform);
  761. cmsCloseProfile(hIn);
  762. cmsCloseProfile(hOut);
  763. if (hProof) cmsCloseProfile(hProof);
  764. return 1;
  765. }
  766. static
  767. void Help(int level)
  768. {
  769. UTILS_UNUSED_PARAMETER(level);
  770. fprintf(stderr, "usage: jpgicc [flags] input.jpg output.jpg\n");
  771. fprintf(stderr, "\nflags:\n\n");
  772. fprintf(stderr, "-v - Verbose\n");
  773. fprintf(stderr, "-i<profile> - Input profile (defaults to sRGB)\n");
  774. fprintf(stderr, "-o<profile> - Output profile (defaults to sRGB)\n");
  775. PrintBuiltins();
  776. PrintRenderingIntents();
  777. fprintf(stderr, "-b - Black point compensation\n");
  778. fprintf(stderr, "-d<0..1> - Observer adaptation state (abs.col. only)\n");
  779. fprintf(stderr, "-n - Ignore embedded profile\n");
  780. fprintf(stderr, "-e - Embed destination profile\n");
  781. fprintf(stderr, "-s<new profile> - Save embedded profile as <new profile>\n");
  782. fprintf(stderr, "\n");
  783. fprintf(stderr, "-c<0,1,2,3> - Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes) [defaults to 1]\n");
  784. fprintf(stderr, "\n");
  785. fprintf(stderr, "-p<profile> - Soft proof profile\n");
  786. fprintf(stderr, "-m<0,1,2,3> - SoftProof intent\n");
  787. fprintf(stderr, "-g - Marks out-of-gamut colors on softproof\n");
  788. fprintf(stderr, "-!<r>,<g>,<b> - Out-of-gamut marker channel values\n");
  789. fprintf(stderr, "\n");
  790. fprintf(stderr, "-q<0..100> - Output JPEG quality\n");
  791. fprintf(stderr, "Examples:\n\n"
  792. "To color correct from scanner to sRGB:\n"
  793. "\tjpgicc -iscanner.icm in.jpg out.jpg\n"
  794. "To convert from monitor1 to monitor2:\n"
  795. "\tjpgicc -imon1.icm -omon2.icm in.jpg out.jpg\n"
  796. "To make a CMYK separation:\n"
  797. "\tjpgicc -oprinter.icm inrgb.jpg outcmyk.jpg\n"
  798. "To recover sRGB from a CMYK separation:\n"
  799. "\tjpgicc -iprinter.icm incmyk.jpg outrgb.jpg\n"
  800. "To convert from CIELab ITU/Fax JPEG to sRGB\n"
  801. "\tjpgicc in.jpg out.jpg\n\n");
  802. fprintf(stderr, "This program is intended to be a demo of the Little CMS\n"
  803. "color engine. Both lcms and this program are open source.\n"
  804. "You can obtain both in source code at https://www.littlecms.com\n"
  805. "For suggestions, comments, bug reports etc. send mail to\n"
  806. "info@littlecms.com\n\n");
  807. exit(0);
  808. }
  809. // The toggles stuff
  810. static
  811. void HandleSwitches(int argc, char *argv[])
  812. {
  813. int s;
  814. while ((s=xgetopt(argc,argv,"bBnNvVGgh:H:i:I:o:O:P:p:t:T:c:C:Q:q:M:m:L:l:eEs:S:!:D:d:-:")) != EOF) {
  815. switch (s)
  816. {
  817. case '-':
  818. if (strcmp(xoptarg, "help") == 0)
  819. {
  820. Help(0);
  821. }
  822. else
  823. {
  824. FatalError("Unknown option - run without args to see valid ones.\n");
  825. }
  826. break;
  827. case 'b':
  828. case 'B':
  829. BlackPointCompensation = TRUE;
  830. break;
  831. case 'd':
  832. case 'D': ObserverAdaptationState = atof(xoptarg);
  833. if (ObserverAdaptationState < 0 ||
  834. ObserverAdaptationState > 1.0)
  835. FatalError("Adaptation state should be 0..1");
  836. break;
  837. case 'v':
  838. case 'V':
  839. Verbose = TRUE;
  840. break;
  841. case 'i':
  842. case 'I':
  843. if (lIsDeviceLink)
  844. FatalError("Device-link already specified");
  845. cInpProf = xoptarg;
  846. break;
  847. case 'o':
  848. case 'O':
  849. if (lIsDeviceLink)
  850. FatalError("Device-link already specified");
  851. cOutProf = xoptarg;
  852. break;
  853. case 'l':
  854. case 'L':
  855. if (cInpProf != NULL || cOutProf != NULL)
  856. FatalError("input/output profiles already specified");
  857. cInpProf = xoptarg;
  858. lIsDeviceLink = TRUE;
  859. break;
  860. case 'p':
  861. case 'P':
  862. cProofing = xoptarg;
  863. break;
  864. case 't':
  865. case 'T':
  866. Intent = atoi(xoptarg);
  867. break;
  868. case 'N':
  869. case 'n':
  870. IgnoreEmbedded = TRUE;
  871. break;
  872. case 'e':
  873. case 'E':
  874. EmbedProfile = TRUE;
  875. break;
  876. case 'g':
  877. case 'G':
  878. GamutCheck = TRUE;
  879. break;
  880. case 'c':
  881. case 'C':
  882. PrecalcMode = atoi(xoptarg);
  883. if (PrecalcMode < 0 || PrecalcMode > 2)
  884. FatalError("Unknown precalc mode '%d'", PrecalcMode);
  885. break;
  886. case 'H':
  887. case 'h': {
  888. int a = atoi(xoptarg);
  889. Help(a);
  890. }
  891. break;
  892. case 'q':
  893. case 'Q':
  894. jpegQuality = atoi(xoptarg);
  895. if (jpegQuality > 100) jpegQuality = 100;
  896. if (jpegQuality < 0) jpegQuality = 0;
  897. break;
  898. case 'm':
  899. case 'M':
  900. ProofingIntent = atoi(xoptarg);
  901. break;
  902. case 's':
  903. case 'S': SaveEmbedded = xoptarg;
  904. break;
  905. case '!':
  906. if (sscanf(xoptarg, "%hu,%hu,%hu", &Alarm[0], &Alarm[1], &Alarm[2]) == 3) {
  907. int i;
  908. for (i=0; i < 3; i++) {
  909. Alarm[i] = (Alarm[i] << 8) | Alarm[i];
  910. }
  911. }
  912. break;
  913. default:
  914. FatalError("Unknown option - run without args to see valid ones");
  915. }
  916. }
  917. }
  918. int main(int argc, char* argv[])
  919. {
  920. fprintf(stderr, "Little CMS ICC profile applier for JPEG - v3.4 [LittleCMS %2.2f]\n\n", cmsGetEncodedCMMversion() / 1000.0);
  921. fprintf(stderr, "Copyright (c) 1998-2023 Marti Maria Saguer. See COPYING file for details.\n");
  922. fflush(stderr);
  923. InitUtils("jpgicc");
  924. HandleSwitches(argc, argv);
  925. if ((argc - xoptind) != 2) {
  926. Help(0);
  927. }
  928. OpenInput(argv[xoptind]);
  929. OpenOutput(argv[xoptind+1]);
  930. TransformImage(cInpProf, cOutProf);
  931. if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }
  932. Done();
  933. return 0;
  934. }