Coverage Report

Created: 2026-02-26 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/splash/SplashScreen.cc
Line
Count
Source
1
//========================================================================
2
//
3
// SplashScreen.cc
4
//
5
// Copyright 2003-2013 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdlib.h>
12
#include <string.h>
13
#if HAVE_STD_SORT
14
#include <algorithm>
15
#endif
16
#include "gmem.h"
17
#include "gmempp.h"
18
#include "SplashMath.h"
19
#include "SplashScreen.h"
20
21
//------------------------------------------------------------------------
22
23
static SplashScreenParams defaultParams = {
24
  splashScreenDispersed,  // type
25
  2,        // size
26
  2,        // dotRadius
27
  1.0,        // gamma
28
  0.0,        // blackThreshold
29
  1.0       // whiteThreshold
30
};
31
32
//------------------------------------------------------------------------
33
34
struct SplashScreenPoint {
35
  int x, y;
36
  int dist;
37
};
38
39
#if HAVE_STD_SORT
40
41
struct cmpDistancesFunctor {
42
49.0M
  bool operator()(const SplashScreenPoint &p0, const SplashScreenPoint &p1) {
43
49.0M
    return p0.dist < p1.dist;
44
49.0M
  }
45
};
46
47
#else // HAVE_STD_SORT
48
49
static int cmpDistances(const void *p0, const void *p1) {
50
  return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist;
51
}
52
53
#endif
54
55
//------------------------------------------------------------------------
56
// SplashScreen
57
//------------------------------------------------------------------------
58
59
// If <clustered> is true, this generates a 45 degree screen using a
60
// circular dot spot function.  DPI = resolution / ((size / 2) *
61
// sqrt(2)).  If <clustered> is false, this generates an optimal
62
// threshold matrix using recursive tesselation.  Gamma correction
63
// (gamma = 1 / 1.33) is also computed here.
64
208k
SplashScreen::SplashScreen(SplashScreenParams *params) {
65
208k
  Guchar u;
66
208k
  int black, white, i;
67
68
208k
  if (!params) {
69
1.53k
    params = &defaultParams;
70
1.53k
  }
71
72
  // size must be a power of 2, and at least 2
73
430k
  for (size = 2, log2Size = 1; size < params->size; size <<= 1, ++log2Size) ;
74
75
208k
  switch (params->type) {
76
77
205k
  case splashScreenDispersed:
78
205k
    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
79
205k
    buildDispersedMatrix(size/2, size/2, 1, size/2, 1);
80
205k
    break;
81
82
0
  case splashScreenClustered:
83
0
    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
84
0
    buildClusteredMatrix();
85
0
    break;
86
87
3.65k
  case splashScreenStochasticClustered:
88
    // size must be at least 2*r
89
3.65k
    while (size < (params->dotRadius << 1)) {
90
0
      size <<= 1;
91
0
      ++log2Size;
92
0
    }
93
3.65k
    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
94
3.65k
    buildSCDMatrix(params->dotRadius);
95
3.65k
    break;
96
208k
  }
97
98
208k
  sizeM1 = size - 1;
99
100
  // do gamma correction and compute minVal/maxVal
101
208k
  minVal = 255;
102
208k
  maxVal = 0;
103
208k
  black = splashRound((SplashCoord)255.0 * params->blackThreshold);
104
208k
  if (black < 1) {
105
208k
    black = 1;
106
208k
  }
107
208k
  white = splashRound((SplashCoord)255.0 * params->whiteThreshold);
108
208k
  if (white > 255) {
109
0
    white = 255;
110
0
  }
111
18.4M
  for (i = 0; i < size * size; ++i) {
112
18.2M
    u = (Guchar)splashRound((SplashCoord)255.0 *
113
18.2M
          splashPow((SplashCoord)mat[i] / 255.0,
114
18.2M
              params->gamma));
115
18.2M
    if (u < black) {
116
0
      u = (Guchar)black;
117
18.2M
    } else if (u >= white) {
118
1.34M
      u = (Guchar)white;
119
1.34M
    }
120
18.2M
    mat[i] = u;
121
18.2M
    if (u < minVal) {
122
425k
      minVal = u;
123
17.8M
    } else if (u > maxVal) {
124
225k
      maxVal = u;
125
225k
    }
126
18.2M
  }
127
208k
}
128
129
void SplashScreen::buildDispersedMatrix(int i, int j, int val,
130
4.28M
          int delta, int offset) {
131
4.28M
  if (delta == 0) {
132
    // map values in [1, size^2] --> [1, 255]
133
3.26M
    mat[(i << log2Size) + j] =
134
3.26M
        (Guchar)(1 + (254 * (val - 1)) / (size * size - 1));
135
3.26M
  } else {
136
1.02M
    buildDispersedMatrix(i, j,
137
1.02M
       val, delta / 2, 4*offset);
138
1.02M
    buildDispersedMatrix((i + delta) % size, (j + delta) % size,
139
1.02M
       val + offset, delta / 2, 4*offset);
140
1.02M
    buildDispersedMatrix((i + delta) % size, j,
141
1.02M
       val + 2*offset, delta / 2, 4*offset);
142
1.02M
    buildDispersedMatrix((i + 2*delta) % size, (j + delta) % size,
143
1.02M
       val + 3*offset, delta / 2, 4*offset);
144
1.02M
  }
145
4.28M
}
146
147
0
void SplashScreen::buildClusteredMatrix() {
148
0
  SplashCoord *dist;
149
0
  SplashCoord u, v, d;
150
0
  Guchar val;
151
0
  int size2, x, y, x1, y1, i;
152
153
0
  size2 = size >> 1;
154
155
  // initialize the threshold matrix
156
0
  for (y = 0; y < size; ++y) {
157
0
    for (x = 0; x < size; ++x) {
158
0
      mat[(y << log2Size) + x] = 0;
159
0
    }
160
0
  }
161
162
  // build the distance matrix
163
0
  dist = (SplashCoord *)gmallocn(size * size2, sizeof(SplashCoord));
164
0
  for (y = 0; y < size2; ++y) {
165
0
    for (x = 0; x < size2; ++x) {
166
0
      if (x + y < size2 - 1) {
167
0
  u = (SplashCoord)x + 0.5 - 0;
168
0
  v = (SplashCoord)y + 0.5 - 0;
169
0
      } else {
170
0
  u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
171
0
  v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
172
0
      }
173
0
      dist[y * size2 + x] = u*u + v*v;
174
0
    }
175
0
  }
176
0
  for (y = 0; y < size2; ++y) {
177
0
    for (x = 0; x < size2; ++x) {
178
0
      if (x < y) {
179
0
  u = (SplashCoord)x + 0.5 - 0;
180
0
  v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
181
0
      } else {
182
0
  u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
183
0
  v = (SplashCoord)y + 0.5 - 0;
184
0
      }
185
0
      dist[(size2 + y) * size2 + x] = u*u + v*v;
186
0
    }
187
0
  }
188
189
  // build the threshold matrix
190
0
  x1 = y1 = 0; // make gcc happy
191
0
  for (i = 0; i < size * size2; ++i) {
192
0
    d = -1;
193
0
    for (y = 0; y < size; ++y) {
194
0
      for (x = 0; x < size2; ++x) {
195
0
  if (mat[(y << log2Size) + x] == 0 &&
196
0
      dist[y * size2 + x] > d) {
197
0
    x1 = x;
198
0
    y1 = y;
199
0
    d = dist[y1 * size2 + x1];
200
0
  }
201
0
      }
202
0
    }
203
    // map values in [0, 2*size*size2-1] --> [1, 255]
204
0
    val = (Guchar)(1 + (254 * (2*i)) / (2*size*size2 - 1));
205
0
    mat[(y1 << log2Size) + x1] = val;
206
0
    val = (Guchar)(1 + (254 * (2*i+1)) / (2*size*size2 - 1));
207
0
    if (y1 < size2) {
208
0
      mat[((y1 + size2) << log2Size) + x1 + size2] = val;
209
0
    } else {
210
0
      mat[((y1 - size2) << log2Size) + x1 + size2] = val;
211
0
    }
212
0
  }
213
214
0
  gfree(dist);
215
0
}
216
217
// Compute the distance between two points on a toroid.
218
4.68G
int SplashScreen::distance(int x0, int y0, int x1, int y1) {
219
4.68G
  int dx0, dx1, dx, dy0, dy1, dy;
220
221
4.68G
  dx0 = abs(x0 - x1);
222
4.68G
  dx1 = size - dx0;
223
4.68G
  dx = dx0 < dx1 ? dx0 : dx1;
224
4.68G
  dy0 = abs(y0 - y1);
225
4.68G
  dy1 = size - dy0;
226
4.68G
  dy = dy0 < dy1 ? dy0 : dy1;
227
4.68G
  return dx * dx + dy * dy;
228
4.68G
}
229
230
// Algorithm taken from:
231
// Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot
232
// Dithering" in Color Imaging: Device-Independent Color, Color
233
// Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999.
234
3.65k
void SplashScreen::buildSCDMatrix(int r) {
235
3.65k
  SplashScreenPoint *dots, *pts;
236
3.65k
  int dotsLen, dotsSize;
237
3.65k
  char *tmpl;
238
3.65k
  char *grid;
239
3.65k
  int *region, *dist;
240
3.65k
  int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n;
241
242
  //~ this should probably happen somewhere else
243
3.65k
  srand(123);
244
245
  // generate the random space-filling curve
246
3.65k
  pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint));
247
3.65k
  i = 0;
248
237k
  for (y = 0; y < size; ++y) {
249
15.2M
    for (x = 0; x < size; ++x) {
250
14.9M
      pts[i].x = x;
251
14.9M
      pts[i].y = y;
252
14.9M
      ++i;
253
14.9M
    }
254
233k
  }
255
14.9M
  for (i = 0; i < size * size; ++i) {
256
14.9M
    j = i + (int)((double)(size * size - i) *
257
14.9M
      (double)rand() / ((double)RAND_MAX + 1.0));
258
14.9M
    x = pts[i].x;
259
14.9M
    y = pts[i].y;
260
14.9M
    pts[i].x = pts[j].x;
261
14.9M
    pts[i].y = pts[j].y;
262
14.9M
    pts[j].x = x;
263
14.9M
    pts[j].y = y;
264
14.9M
  }
265
266
  // construct the circle template
267
3.65k
  tmpl = (char *)gmallocn((r+1)*(r+1), sizeof(char));
268
14.6k
  for (y = 0; y <= r; ++y) {
269
43.8k
    for (x = 0; x <= r; ++x) {
270
32.8k
      tmpl[y*(r+1) + x] = (x * y <= r * r) ? 1 : 0;
271
32.8k
    }
272
10.9k
  }
273
274
  // mark all grid cells as free
275
3.65k
  grid = (char *)gmallocn(size * size, sizeof(char));
276
237k
  for (y = 0; y < size; ++y) {
277
15.2M
    for (x = 0; x < size; ++x) {
278
14.9M
      grid[(y << log2Size) + x] = 0;
279
14.9M
    }
280
233k
  }
281
282
  // walk the space-filling curve, adding dots
283
3.65k
  dotsLen = 0;
284
3.65k
  dotsSize = 32;
285
3.65k
  dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint));
286
14.9M
  for (i = 0; i < size * size; ++i) {
287
14.9M
    x = pts[i].x;
288
14.9M
    y = pts[i].y;
289
14.9M
    if (!grid[(y << log2Size) + x]) {
290
1.14M
      if (dotsLen == dotsSize) {
291
14.6k
  dotsSize *= 2;
292
14.6k
  dots = (SplashScreenPoint *)greallocn(dots, dotsSize,
293
14.6k
                sizeof(SplashScreenPoint));
294
14.6k
      }
295
1.14M
      dots[dotsLen++] = pts[i];
296
4.56M
      for (yy = 0; yy <= r; ++yy) {
297
3.42M
  y0 = (y + yy) % size;
298
3.42M
  y1 = (y - yy + size) % size;
299
13.6M
  for (xx = 0; xx <= r; ++xx) {
300
10.2M
    if (tmpl[yy*(r+1) + xx]) {
301
10.2M
      x0 = (x + xx) % size;
302
10.2M
      x1 = (x - xx + size) % size;
303
10.2M
      grid[(y0 << log2Size) + x0] = 1;
304
10.2M
      grid[(y0 << log2Size) + x1] = 1;
305
10.2M
      grid[(y1 << log2Size) + x0] = 1;
306
10.2M
      grid[(y1 << log2Size) + x1] = 1;
307
10.2M
    }
308
10.2M
  }
309
3.42M
      }
310
1.14M
    }
311
14.9M
  }
312
313
3.65k
  gfree(tmpl);
314
3.65k
  gfree(grid);
315
316
  // assign each cell to a dot, compute distance to center of dot
317
3.65k
  region = (int *)gmallocn(size * size, sizeof(int));
318
3.65k
  dist = (int *)gmallocn(size * size, sizeof(int));
319
237k
  for (y = 0; y < size; ++y) {
320
15.2M
    for (x = 0; x < size; ++x) {
321
14.9M
      iMin = 0;
322
14.9M
      dMin = distance(dots[0].x, dots[0].y, x, y);
323
4.66G
      for (i = 1; i < dotsLen; ++i) {
324
4.65G
  d = distance(dots[i].x, dots[i].y, x, y);
325
4.65G
  if (d < dMin) {
326
76.7M
    iMin = i;
327
76.7M
    dMin = d;
328
76.7M
  }
329
4.65G
      }
330
14.9M
      region[(y << log2Size) + x] = iMin;
331
14.9M
      dist[(y << log2Size) + x] = dMin;
332
14.9M
    }
333
233k
  }
334
335
  // compute threshold values
336
1.14M
  for (i = 0; i < dotsLen; ++i) {
337
1.14M
    n = 0;
338
74.1M
    for (y = 0; y < size; ++y) {
339
4.74G
      for (x = 0; x < size; ++x) {
340
4.66G
  if (region[(y << log2Size) + x] == i) {
341
14.9M
    pts[n].x = x;
342
14.9M
    pts[n].y = y;
343
14.9M
    pts[n].dist = distance(dots[i].x, dots[i].y, x, y);
344
14.9M
    ++n;
345
14.9M
  }
346
4.66G
      }
347
72.9M
    }
348
1.14M
#if HAVE_STD_SORT
349
1.14M
    std::sort(pts, pts + n, cmpDistancesFunctor());
350
#else
351
    qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances);
352
#endif
353
16.1M
    for (j = 0; j < n; ++j) {
354
      // map values in [0 .. n-1] --> [255 .. 1]
355
14.9M
      mat[(pts[j].y << log2Size) + pts[j].x] =
356
14.9M
    (Guchar)(255 - (254 * j) / (n - 1));
357
14.9M
    }
358
1.14M
  }
359
360
3.65k
  gfree(pts);
361
3.65k
  gfree(region);
362
3.65k
  gfree(dist);
363
364
3.65k
  gfree(dots);
365
3.65k
}
366
367
2.82M
SplashScreen::SplashScreen(SplashScreen *screen) {
368
2.82M
  size = screen->size;
369
2.82M
  sizeM1 = screen->sizeM1;
370
2.82M
  log2Size = screen->log2Size;
371
2.82M
  mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
372
2.82M
  memcpy(mat, screen->mat, size * size * sizeof(Guchar));
373
2.82M
  minVal = screen->minVal;
374
2.82M
  maxVal = screen->maxVal;
375
2.82M
}
376
377
3.03M
SplashScreen::~SplashScreen() {
378
3.03M
  gfree(mat);
379
3.03M
}