1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the Chromium LICENSE file.
4 
5 #include "qcms.h"
6 #include "qcms_test_util.h"
7 
8 #include <assert.h>
9 #include <math.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <time.h>
13 
14 #define PARAMETRIC_CURVE_TYPE 0x70617261 // 'para'
15 
16 static const float inverse65535 = (float) (1.0 / 65535.0);
17 
18 extern float clamp_float(float a);
19 
get_output_gamma_table(const char * profile_path,uint16_t ** table,size_t * size)20 static int get_output_gamma_table(const char *profile_path, uint16_t **table, size_t *size)
21 {
22     qcms_transform *transform;
23     qcms_profile *sRGB;
24     qcms_profile *target;
25 
26     target = qcms_profile_from_path(profile_path);
27     if (!target) {
28         fprintf(stderr, "Invalid input profile\n");
29         return EXIT_FAILURE;
30     }
31 
32     sRGB = qcms_profile_sRGB();
33 
34     transform = qcms_transform_create(sRGB, QCMS_DATA_RGBA_8, target, QCMS_DATA_RGBA_8, QCMS_INTENT_DEFAULT);
35     if (!transform) {
36         fprintf(stderr, "Failed to create colour transform\n");
37         qcms_profile_release(sRGB);
38         qcms_profile_release(target);
39         return EXIT_FAILURE;
40     }
41 
42     *size = qcms_transform_get_output_trc_rgba(transform, target, QCMS_TRC_USHORT, NULL);
43     assert(*size >= 256);
44 
45     *table = malloc(*size * sizeof(uint16_t) * 4);
46     qcms_transform_get_output_trc_rgba(transform, target, QCMS_TRC_USHORT, *table);
47 
48     qcms_transform_release(transform);
49     qcms_profile_release(sRGB);
50     qcms_profile_release(target);
51 
52     return 0;
53 }
54 
get_input_gamma_table(const char * profile_path,uint16_t ** table,size_t * size)55 static int get_input_gamma_table(const char *profile_path, uint16_t **table, size_t *size)
56 {
57     qcms_transform *transform;
58     qcms_profile *source;
59     qcms_profile *sRGB;
60 
61     source = qcms_profile_from_path(profile_path);
62     if (!source) {
63         fprintf(stderr, "Invalid input profile\n");
64         return EXIT_FAILURE;
65     }
66 
67     sRGB = qcms_profile_sRGB();
68 
69     transform = qcms_transform_create(source, QCMS_DATA_RGBA_8, sRGB, QCMS_DATA_RGBA_8, QCMS_INTENT_DEFAULT);
70     if (!transform) {
71         fprintf(stderr, "Failed to create colour transform\n");
72         qcms_profile_release(sRGB);
73         qcms_profile_release(source);
74         return EXIT_FAILURE;
75     }
76 
77     *size = qcms_transform_get_input_trc_rgba(transform, source, QCMS_TRC_USHORT, NULL);
78     assert(*size >= 256);
79 
80     *table = calloc(*size, sizeof(uint16_t) * 4);
81     qcms_transform_get_input_trc_rgba(transform, source, QCMS_TRC_USHORT, *table);
82 
83     qcms_transform_release(transform);
84     qcms_profile_release(sRGB);
85     qcms_profile_release(source);
86 
87     return 0;
88 }
89 
qcms_test_output_trc(size_t width,size_t height,int iterations,const char * in_path,const char * out_path,const int force_software)90 static int qcms_test_output_trc(size_t width,
91         size_t height,
92         int iterations,
93         const char *in_path,
94         const char *out_path,
95         const int force_software)
96 {
97     uint16_t *gamma_table_out = NULL;
98     size_t output_size = 0;
99     qcms_profile *profile;
100     long time_stamp = (long)time(NULL);
101     char output_file_name[1024];
102     float scale_factor;
103     size_t i;
104 
105     if (!in_path) {
106         fprintf(stderr, "%s: please provide valid ICC profiles via -i option\n", __FUNCTION__);
107         return EXIT_FAILURE;
108     }
109 
110     printf("Test color profile gamma curves\n");
111     fflush(stdout);
112 
113     if (get_output_gamma_table(in_path, &gamma_table_out, &output_size) != 0) {
114         fprintf(stderr, "Unable to extract output gamma table\n");
115         return EXIT_FAILURE;
116     }
117 
118     printf("Output gamma table size = %zu\n", output_size);
119 
120     profile = qcms_profile_from_path(in_path);
121     if (!profile) {
122         fprintf(stderr, "Invalid input profile\n");
123         free(gamma_table_out);
124         return EXIT_FAILURE;
125     }
126 
127     if (profile->redTRC->type == PARAMETRIC_CURVE_TYPE) {
128         // Check the red TRC curve only for now.
129         int type = - (int)(profile->redTRC->count + 1);
130         uint16_t *gamma_table_in = NULL;
131         size_t input_size = 0;
132         FILE *output_file;
133 
134         printf("Detected parametric curve type = %d\n", profile->redTRC->count);
135 
136         if (get_input_gamma_table(in_path, &gamma_table_in, &input_size) != 0) {
137             fprintf(stderr, "Failed to compute input gamma table\n");
138             qcms_profile_release(profile);
139             free(gamma_table_out);
140             return EXIT_FAILURE;
141         }
142 
143         // Write output to stdout and tables into a csv file.
144         sprintf(output_file_name, "qcms-test-%ld-parametric-gamma-output-%s.csv",
145                 time_stamp, profile->description);
146         printf("Writing output gamma tables to %s\n", output_file_name);
147         output_file = fopen(output_file_name, "w");
148 
149         printf("Parametric gamma values for profile %s description [%s]\n",
150                in_path, profile->description);
151         fprintf(output_file, "Parametric gamma values for profile %s description [%s]\n",
152                 in_path, profile->description);
153 
154         printf("gamma = %.6f, a = %.6f, b = %.6f, c = %.6f, d = %.6f, e = %.6f, f = %.6f\n",
155                 profile->redTRC->parameter[0], profile->redTRC->parameter[1], profile->redTRC->parameter[2],
156                 profile->redTRC->parameter[3], profile->redTRC->parameter[4], profile->redTRC->parameter[5],
157                 profile->redTRC->parameter[6]);
158 
159         fprintf(output_file, "gamma, a, b, c, d, e, f\n");
160         fprintf(output_file, "%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f\n",
161                 profile->redTRC->parameter[0], profile->redTRC->parameter[1], profile->redTRC->parameter[2],
162                 profile->redTRC->parameter[3], profile->redTRC->parameter[4], profile->redTRC->parameter[5],
163                 profile->redTRC->parameter[6]);
164 
165         fprintf(output_file, "\nInput curve size: %zu", input_size);
166         fprintf(output_file, "\nOutput curve size: %zu", output_size);
167 
168         fprintf(output_file, "\n\nInput gamma, Output gamma, LCMS Output gamma, Output gamma error\n");
169         // Output gamma curve down-sample factor.
170         scale_factor = (float)(output_size - 1) / (input_size - 1);
171 
172         for (i = 0; i < input_size; ++i) {
173             float input = gamma_table_in[i * 4] * inverse65535;
174             size_t out_index = (size_t)floor(i * scale_factor + 0.5);
175             float output = gamma_table_out[out_index * 4] * inverse65535;
176             float x = out_index / (float)(output_size - 1);
177             float reference = clamp_float(evaluate_parametric_curve(type, profile->redTRC->parameter, x));
178             float difference = fabs(output - reference);
179 
180             fprintf(output_file, "%.6f, %.6f, %6f, %6f\n", input, output, reference, difference);
181         }
182 
183         fprintf(output_file, "\nNote: the output gamma curves are down-sampled by a factor of %zu / %zu\n",
184                 output_size, input_size);
185 
186         fclose(output_file);
187         free(gamma_table_in);
188     } else {
189         uint16_t *gamma_table_in = NULL;
190         size_t input_size = 0;
191         FILE *output_file;
192 
193         if (get_input_gamma_table(in_path, &gamma_table_in, &input_size) != 0) {
194             fprintf(stderr, "Failed to compute input gamma table\n");
195             qcms_profile_release(profile);
196             free(gamma_table_out);
197             return EXIT_FAILURE;
198         }
199 
200         // Write output to stdout and tables into a csv file.
201         sprintf(output_file_name, "qcms-test-%ld-gamma-output-%s.csv",
202                 time_stamp, profile->description);
203         printf("Writing gamma tables to %s\n", output_file_name);
204         output_file = fopen(output_file_name, "w");
205 
206         printf("Gamma values for profile %s description [%s]\n",
207                in_path, profile->description);
208         fprintf(output_file, "Gamma values for profile %s description [%s]\n",
209                 in_path, profile->description);
210 
211         if (profile->redTRC->count == 0) {
212             printf("Gamma LUT type 0: linear gamma\n");
213             fprintf(output_file, "Gamma LUT type 0: linear gamma\n");
214         } else if (profile->redTRC->count == 1) {
215             float gamma = profile->redTRC->data[0] / 256.0f;
216             printf("Gamma LUT type 1: gamma = %.6f\n", gamma);
217             fprintf(output_file, "Gamma LUT type 1: gamma = %.6f\n", gamma);
218         } else {
219             printf("Gamma LUT table size = %u\n", profile->redTRC->count);
220             fprintf(output_file, "Gamma LUT table size = %u\n", profile->redTRC->count);
221         }
222 
223         fprintf(output_file, "\nInput curve size: %zu", input_size);
224         fprintf(output_file, "\nOutput curve size: %zu", output_size);
225 
226         fprintf(output_file, "\n\nInput gamma, Output gamma\n");
227         // Output gamma curve down-sample factor.
228         scale_factor = (float)(output_size - 1) / (input_size - 1);
229 
230         for (i = 0; i < input_size; ++i) {
231             float input = gamma_table_in[i * 4] * inverse65535;
232             size_t out_index = (size_t)floor(i * scale_factor + 0.5);
233             float output = gamma_table_out[out_index * 4] * inverse65535;
234 
235             fprintf(output_file, "%.6f, %.6f\n", input, output);
236         }
237 
238         fprintf(output_file, "\nNote: the output gamma curves are down-sampled by a factor of %zu / %zu\n",
239                 output_size, input_size);
240 
241         fclose(output_file);
242         free(gamma_table_in);
243     }
244 
245     qcms_profile_release(profile);
246     free(gamma_table_out);
247 
248     return 0;
249 }
250 
251 struct qcms_test_case qcms_test_output_trc_info = {
252         "qcms_test_output_trc",
253         qcms_test_output_trc,
254         QCMS_TEST_DISABLED
255 };
256