xref: /aosp_15_r20/external/brotli/research/draw_histogram.cc (revision f4ee7fba7774faf2a30f13154332c0a06550dbc4)
1*f4ee7fbaSAndroid Build Coastguard Worker /* Copyright 2016 Google Inc. All Rights Reserved.
2*f4ee7fbaSAndroid Build Coastguard Worker    Author: [email protected] (Ivan Nikulin)
3*f4ee7fbaSAndroid Build Coastguard Worker 
4*f4ee7fbaSAndroid Build Coastguard Worker    Distributed under MIT license.
5*f4ee7fbaSAndroid Build Coastguard Worker    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
6*f4ee7fbaSAndroid Build Coastguard Worker */
7*f4ee7fbaSAndroid Build Coastguard Worker 
8*f4ee7fbaSAndroid Build Coastguard Worker /* Backward reference visualization tool. Accepts file with backward references
9*f4ee7fbaSAndroid Build Coastguard Worker    as an input and produces PGM image with histogram of those references. */
10*f4ee7fbaSAndroid Build Coastguard Worker 
11*f4ee7fbaSAndroid Build Coastguard Worker #include <algorithm> /* min */
12*f4ee7fbaSAndroid Build Coastguard Worker #include <cassert>
13*f4ee7fbaSAndroid Build Coastguard Worker #include <cstring> /* memset */
14*f4ee7fbaSAndroid Build Coastguard Worker #include <cmath> /* log, round */
15*f4ee7fbaSAndroid Build Coastguard Worker #include <cstdio> /* fscanf, fprintf */
16*f4ee7fbaSAndroid Build Coastguard Worker #include <cstdint>
17*f4ee7fbaSAndroid Build Coastguard Worker 
18*f4ee7fbaSAndroid Build Coastguard Worker #include <gflags/gflags.h>
19*f4ee7fbaSAndroid Build Coastguard Worker using gflags::ParseCommandLineFlags;
20*f4ee7fbaSAndroid Build Coastguard Worker 
21*f4ee7fbaSAndroid Build Coastguard Worker #include "./read_dist.h"
22*f4ee7fbaSAndroid Build Coastguard Worker 
23*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_int32(height, 1000, "Height of the resulting histogam.");
24*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_int32(width, 8000, "Width of the resulting histogam.");
25*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_int32(size, 1e8, "Size of the compressed file.");
26*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_int32(brotli_window, -1, "Size of brotli window in bits.");
27*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_uint64(min_distance, 0, "Minimum distance.");
28*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_uint64(max_distance, 1 << 30, "Maximum distance.");
29*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_bool(with_copies, false, "True if input contains copy length.");
30*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_bool(simple, false, "True if using only black and white pixels.");
31*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_bool(linear, false, "True if using linear distance mapping.");
32*f4ee7fbaSAndroid Build Coastguard Worker DEFINE_uint64(skip, 0, "Number of bytes to skip.");
33*f4ee7fbaSAndroid Build Coastguard Worker 
DistanceTransform(double x)34*f4ee7fbaSAndroid Build Coastguard Worker inline double DistanceTransform(double x) {
35*f4ee7fbaSAndroid Build Coastguard Worker   static bool linear = FLAGS_linear;
36*f4ee7fbaSAndroid Build Coastguard Worker   if (linear) {
37*f4ee7fbaSAndroid Build Coastguard Worker     return x;
38*f4ee7fbaSAndroid Build Coastguard Worker   } else {
39*f4ee7fbaSAndroid Build Coastguard Worker     /* Using log^2 scale because log scale produces big white gap at the bottom
40*f4ee7fbaSAndroid Build Coastguard Worker        of image. */
41*f4ee7fbaSAndroid Build Coastguard Worker     return log(x) * log(x);
42*f4ee7fbaSAndroid Build Coastguard Worker   }
43*f4ee7fbaSAndroid Build Coastguard Worker }
44*f4ee7fbaSAndroid Build Coastguard Worker 
45*f4ee7fbaSAndroid Build Coastguard Worker /* Mapping pixel density on arc function to increase contrast. */
DensityTransform(double x)46*f4ee7fbaSAndroid Build Coastguard Worker inline double DensityTransform(double x) {
47*f4ee7fbaSAndroid Build Coastguard Worker   double z = 255 - x;
48*f4ee7fbaSAndroid Build Coastguard Worker   return sqrt(255 * 255 - z * z);
49*f4ee7fbaSAndroid Build Coastguard Worker }
50*f4ee7fbaSAndroid Build Coastguard Worker 
GetMaxDistance()51*f4ee7fbaSAndroid Build Coastguard Worker inline int GetMaxDistance() {
52*f4ee7fbaSAndroid Build Coastguard Worker   return FLAGS_max_distance;
53*f4ee7fbaSAndroid Build Coastguard Worker }
54*f4ee7fbaSAndroid Build Coastguard Worker 
AdjustPosition(int * pos)55*f4ee7fbaSAndroid Build Coastguard Worker void AdjustPosition(int* pos) {
56*f4ee7fbaSAndroid Build Coastguard Worker   static uint32_t offset = 0;
57*f4ee7fbaSAndroid Build Coastguard Worker   static int last = 0;
58*f4ee7fbaSAndroid Build Coastguard Worker   static uint32_t window_size = (1 << FLAGS_brotli_window);
59*f4ee7fbaSAndroid Build Coastguard Worker   assert(*pos >= 0 && *pos < window_size);
60*f4ee7fbaSAndroid Build Coastguard Worker   if (*pos < last) {
61*f4ee7fbaSAndroid Build Coastguard Worker     offset += window_size;
62*f4ee7fbaSAndroid Build Coastguard Worker   }
63*f4ee7fbaSAndroid Build Coastguard Worker   last = *pos;
64*f4ee7fbaSAndroid Build Coastguard Worker   *pos += offset;
65*f4ee7fbaSAndroid Build Coastguard Worker }
66*f4ee7fbaSAndroid Build Coastguard Worker 
BuildHistogram(FILE * fin,int ** histo)67*f4ee7fbaSAndroid Build Coastguard Worker void BuildHistogram(FILE* fin, int** histo) {
68*f4ee7fbaSAndroid Build Coastguard Worker   int height = FLAGS_height;
69*f4ee7fbaSAndroid Build Coastguard Worker   int width = FLAGS_width;
70*f4ee7fbaSAndroid Build Coastguard Worker   int skip = FLAGS_skip;
71*f4ee7fbaSAndroid Build Coastguard Worker   size_t min_distance = FLAGS_min_distance;
72*f4ee7fbaSAndroid Build Coastguard Worker 
73*f4ee7fbaSAndroid Build Coastguard Worker   printf("height = %d, width = %d\n", height, width);
74*f4ee7fbaSAndroid Build Coastguard Worker 
75*f4ee7fbaSAndroid Build Coastguard Worker   for (int i = 0; i < height; i++) {
76*f4ee7fbaSAndroid Build Coastguard Worker     for (int j = 0; j < width; j++) {
77*f4ee7fbaSAndroid Build Coastguard Worker       histo[i][j] = 0;
78*f4ee7fbaSAndroid Build Coastguard Worker     }
79*f4ee7fbaSAndroid Build Coastguard Worker   }
80*f4ee7fbaSAndroid Build Coastguard Worker 
81*f4ee7fbaSAndroid Build Coastguard Worker   int max_pos = FLAGS_size - skip;
82*f4ee7fbaSAndroid Build Coastguard Worker   double min_dist = min_distance > 0 ? DistanceTransform(min_distance) : 0;
83*f4ee7fbaSAndroid Build Coastguard Worker   double max_dist = DistanceTransform(GetMaxDistance()) - min_dist;
84*f4ee7fbaSAndroid Build Coastguard Worker   int copy, pos, distance, x, y;
85*f4ee7fbaSAndroid Build Coastguard Worker   double dist;
86*f4ee7fbaSAndroid Build Coastguard Worker   while (ReadBackwardReference(fin, &copy, &pos, &distance)) {
87*f4ee7fbaSAndroid Build Coastguard Worker     if (pos == -1) continue;  // In case when only insert is present.
88*f4ee7fbaSAndroid Build Coastguard Worker     if (distance < min_distance || distance >= GetMaxDistance()) continue;
89*f4ee7fbaSAndroid Build Coastguard Worker     if (FLAGS_brotli_window != -1) {
90*f4ee7fbaSAndroid Build Coastguard Worker       AdjustPosition(&pos);
91*f4ee7fbaSAndroid Build Coastguard Worker     }
92*f4ee7fbaSAndroid Build Coastguard Worker     if (pos >= skip && distance <= pos) {
93*f4ee7fbaSAndroid Build Coastguard Worker       pos -= skip;
94*f4ee7fbaSAndroid Build Coastguard Worker       if (pos >= max_pos) break;
95*f4ee7fbaSAndroid Build Coastguard Worker       dist = DistanceTransform(static_cast<double>(distance)) - min_dist;
96*f4ee7fbaSAndroid Build Coastguard Worker 
97*f4ee7fbaSAndroid Build Coastguard Worker       x = std::min(static_cast<int>(round(dist / max_dist * height)),
98*f4ee7fbaSAndroid Build Coastguard Worker                    height - 1);
99*f4ee7fbaSAndroid Build Coastguard Worker       y = 1ul * pos * width / max_pos;
100*f4ee7fbaSAndroid Build Coastguard Worker       if (!(y >= 0 && y < width)) {
101*f4ee7fbaSAndroid Build Coastguard Worker         printf("pos = %d, max_pos = %d, y = %d\n", pos, max_pos, y);
102*f4ee7fbaSAndroid Build Coastguard Worker         assert(y >= 0 && y < width);
103*f4ee7fbaSAndroid Build Coastguard Worker       }
104*f4ee7fbaSAndroid Build Coastguard Worker 
105*f4ee7fbaSAndroid Build Coastguard Worker       if (FLAGS_with_copies) {
106*f4ee7fbaSAndroid Build Coastguard Worker         int right = 1ul * (pos + copy - 1) * width / max_pos;
107*f4ee7fbaSAndroid Build Coastguard Worker         if (right < 0) {
108*f4ee7fbaSAndroid Build Coastguard Worker           printf("pos = %d, distance = %d, copy = %d, y = %d, right = %d\n",
109*f4ee7fbaSAndroid Build Coastguard Worker                   pos, distance, copy, y, right);
110*f4ee7fbaSAndroid Build Coastguard Worker           assert(right >= 0);
111*f4ee7fbaSAndroid Build Coastguard Worker         }
112*f4ee7fbaSAndroid Build Coastguard Worker         if (y == right) {
113*f4ee7fbaSAndroid Build Coastguard Worker           histo[x][y] += copy;
114*f4ee7fbaSAndroid Build Coastguard Worker         } else {
115*f4ee7fbaSAndroid Build Coastguard Worker           int pos2 = static_cast<int>(ceil(1.0 * (y + 1) * max_pos / width));
116*f4ee7fbaSAndroid Build Coastguard Worker           histo[x][y] += pos2 - pos;
117*f4ee7fbaSAndroid Build Coastguard Worker           for (int i = y + 1; i < right && i < width; ++i) {
118*f4ee7fbaSAndroid Build Coastguard Worker             histo[x][i] += max_pos / width;  // Sometimes 1 more, but who cares.
119*f4ee7fbaSAndroid Build Coastguard Worker           }
120*f4ee7fbaSAndroid Build Coastguard Worker           // Make sure the match doesn't go beyond the image.
121*f4ee7fbaSAndroid Build Coastguard Worker           if (right < width) {
122*f4ee7fbaSAndroid Build Coastguard Worker             pos2 = static_cast<int>(ceil(1.0 * right * max_pos / width));
123*f4ee7fbaSAndroid Build Coastguard Worker             histo[x][right] += pos + copy - 1 - pos2 + 1;
124*f4ee7fbaSAndroid Build Coastguard Worker           }
125*f4ee7fbaSAndroid Build Coastguard Worker         }
126*f4ee7fbaSAndroid Build Coastguard Worker       } else {
127*f4ee7fbaSAndroid Build Coastguard Worker         histo[x][y]++;
128*f4ee7fbaSAndroid Build Coastguard Worker       }
129*f4ee7fbaSAndroid Build Coastguard Worker     }
130*f4ee7fbaSAndroid Build Coastguard Worker   }
131*f4ee7fbaSAndroid Build Coastguard Worker }
132*f4ee7fbaSAndroid Build Coastguard Worker 
ConvertToPixels(int ** histo,uint8_t ** pixel)133*f4ee7fbaSAndroid Build Coastguard Worker void ConvertToPixels(int** histo, uint8_t** pixel) {
134*f4ee7fbaSAndroid Build Coastguard Worker   int height = FLAGS_height;
135*f4ee7fbaSAndroid Build Coastguard Worker   int width = FLAGS_width;
136*f4ee7fbaSAndroid Build Coastguard Worker 
137*f4ee7fbaSAndroid Build Coastguard Worker   int maxs = 0;
138*f4ee7fbaSAndroid Build Coastguard Worker   for (int i = 0; i < height; i++) {
139*f4ee7fbaSAndroid Build Coastguard Worker     for (int j = 0; j < width; j++) {
140*f4ee7fbaSAndroid Build Coastguard Worker       if (maxs < histo[i][j]) maxs = histo[i][j];
141*f4ee7fbaSAndroid Build Coastguard Worker     }
142*f4ee7fbaSAndroid Build Coastguard Worker   }
143*f4ee7fbaSAndroid Build Coastguard Worker 
144*f4ee7fbaSAndroid Build Coastguard Worker   bool simple = FLAGS_simple;
145*f4ee7fbaSAndroid Build Coastguard Worker   double max_histo = static_cast<double>(maxs);
146*f4ee7fbaSAndroid Build Coastguard Worker   for (int i = 0; i < height; i++) {
147*f4ee7fbaSAndroid Build Coastguard Worker     for (int j = 0; j < width; j++) {
148*f4ee7fbaSAndroid Build Coastguard Worker       if (simple) {
149*f4ee7fbaSAndroid Build Coastguard Worker         pixel[i][j] = histo[i][j] > 0 ? 0 : 255;
150*f4ee7fbaSAndroid Build Coastguard Worker       } else {
151*f4ee7fbaSAndroid Build Coastguard Worker         pixel[i][j] = static_cast<uint8_t>(
152*f4ee7fbaSAndroid Build Coastguard Worker             255 - DensityTransform(histo[i][j] / max_histo * 255));
153*f4ee7fbaSAndroid Build Coastguard Worker       }
154*f4ee7fbaSAndroid Build Coastguard Worker     }
155*f4ee7fbaSAndroid Build Coastguard Worker   }
156*f4ee7fbaSAndroid Build Coastguard Worker }
157*f4ee7fbaSAndroid Build Coastguard Worker 
DrawPixels(uint8_t ** pixel,FILE * fout)158*f4ee7fbaSAndroid Build Coastguard Worker void DrawPixels(uint8_t** pixel, FILE* fout) {
159*f4ee7fbaSAndroid Build Coastguard Worker   int height = FLAGS_height;
160*f4ee7fbaSAndroid Build Coastguard Worker   int width = FLAGS_width;
161*f4ee7fbaSAndroid Build Coastguard Worker 
162*f4ee7fbaSAndroid Build Coastguard Worker   fprintf(fout, "P5\n%d %d\n255\n", width, height);
163*f4ee7fbaSAndroid Build Coastguard Worker   for (int i = height - 1; i >= 0; i--) {
164*f4ee7fbaSAndroid Build Coastguard Worker     fwrite(pixel[i], 1, width, fout);
165*f4ee7fbaSAndroid Build Coastguard Worker   }
166*f4ee7fbaSAndroid Build Coastguard Worker }
167*f4ee7fbaSAndroid Build Coastguard Worker 
main(int argc,char * argv[])168*f4ee7fbaSAndroid Build Coastguard Worker int main(int argc, char* argv[]) {
169*f4ee7fbaSAndroid Build Coastguard Worker   ParseCommandLineFlags(&argc, &argv, true);
170*f4ee7fbaSAndroid Build Coastguard Worker   if (argc != 3) {
171*f4ee7fbaSAndroid Build Coastguard Worker     printf("usage: draw_histogram.cc data output_file\n");
172*f4ee7fbaSAndroid Build Coastguard Worker     return 1;
173*f4ee7fbaSAndroid Build Coastguard Worker   }
174*f4ee7fbaSAndroid Build Coastguard Worker 
175*f4ee7fbaSAndroid Build Coastguard Worker   int height = FLAGS_height;
176*f4ee7fbaSAndroid Build Coastguard Worker   int width = FLAGS_width;
177*f4ee7fbaSAndroid Build Coastguard Worker 
178*f4ee7fbaSAndroid Build Coastguard Worker   FILE* fin = fopen(argv[1], "r");
179*f4ee7fbaSAndroid Build Coastguard Worker   FILE* fout = fopen(argv[2], "wb");
180*f4ee7fbaSAndroid Build Coastguard Worker 
181*f4ee7fbaSAndroid Build Coastguard Worker   if (fin != nullptr && fout != nullptr) {
182*f4ee7fbaSAndroid Build Coastguard Worker     uint8_t** pixel = new uint8_t*[height];
183*f4ee7fbaSAndroid Build Coastguard Worker     int** histo = new int*[height];
184*f4ee7fbaSAndroid Build Coastguard Worker     for (int i = 0; i < height; i++) {
185*f4ee7fbaSAndroid Build Coastguard Worker       pixel[i] = new uint8_t[width];
186*f4ee7fbaSAndroid Build Coastguard Worker       histo[i] = new int[width];
187*f4ee7fbaSAndroid Build Coastguard Worker     }
188*f4ee7fbaSAndroid Build Coastguard Worker 
189*f4ee7fbaSAndroid Build Coastguard Worker     BuildHistogram(fin, histo);
190*f4ee7fbaSAndroid Build Coastguard Worker 
191*f4ee7fbaSAndroid Build Coastguard Worker     ConvertToPixels(histo, pixel);
192*f4ee7fbaSAndroid Build Coastguard Worker 
193*f4ee7fbaSAndroid Build Coastguard Worker     DrawPixels(pixel, fout);
194*f4ee7fbaSAndroid Build Coastguard Worker   }
195*f4ee7fbaSAndroid Build Coastguard Worker 
196*f4ee7fbaSAndroid Build Coastguard Worker   if (fin) fclose(fin);
197*f4ee7fbaSAndroid Build Coastguard Worker   if (fout) fclose(fout);
198*f4ee7fbaSAndroid Build Coastguard Worker 
199*f4ee7fbaSAndroid Build Coastguard Worker   return 0;
200*f4ee7fbaSAndroid Build Coastguard Worker }
201