1
//========================================================================
5
//========================================================================
7
//========================================================================
9
// Modified under the Poppler project - http://poppler.freedesktop.org
11
// All changes made under the Poppler project to this file are licensed
12
// under GPL version 2 or later
14
// Copyright (C) 2009 Albert Astals Cid <aacid@kde.org>
16
// To see a description of the changes please see the Changelog file that
17
// came with your tarball or type make ChangeLog if you are building from git
19
//========================================================================
23
#ifdef USE_GCC_PRAGMAS
24
#pragma implementation
30
#include "SplashMath.h"
31
#include "SplashScreen.h"
33
static SplashScreenParams defaultParams = {
34
splashScreenDispersed, // type
38
0.0, // blackThreshold
42
//------------------------------------------------------------------------
44
struct SplashScreenPoint {
49
static int cmpDistances(const void *p0, const void *p1) {
50
return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist;
53
//------------------------------------------------------------------------
55
//------------------------------------------------------------------------
57
// If <clustered> is true, this generates a 45 degree screen using a
58
// circular dot spot function. DPI = resolution / ((size / 2) *
59
// sqrt(2)). If <clustered> is false, this generates an optimal
60
// threshold matrix using recursive tesselation. Gamma correction
61
// (gamma = 1 / 1.33) is also computed here.
62
SplashScreen::SplashScreen(SplashScreenParams *params) {
65
params = &defaultParams;
68
screenParams = params;
75
void SplashScreen::createMatrix()
77
Guchar u, black, white;
80
SplashScreenParams *params = screenParams;
82
switch (params->type) {
84
case splashScreenDispersed:
85
// size must be a power of 2
86
for (size = 1; size < params->size; size <<= 1) ;
87
mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
88
buildDispersedMatrix(size/2, size/2, 1, size/2, 1);
91
case splashScreenClustered:
93
size = (params->size >> 1) << 1;
97
mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
98
buildClusteredMatrix();
101
case splashScreenStochasticClustered:
102
// size must be at least 2*r
103
if (params->size < 2 * params->dotRadius) {
104
size = 2 * params->dotRadius;
108
mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
109
buildSCDMatrix(params->dotRadius);
113
// do gamma correction and compute minVal/maxVal
116
black = splashRound((SplashCoord)255.0 * params->blackThreshold);
120
int whiteAux = splashRound((SplashCoord)255.0 * params->whiteThreshold);
121
if (whiteAux > 255) {
126
for (i = 0; i < size * size; ++i) {
127
u = splashRound((SplashCoord)255.0 *
128
splashPow((SplashCoord)mat[i] / 255.0, params->gamma));
131
} else if (u >= white) {
137
} else if (u > maxVal) {
143
void SplashScreen::buildDispersedMatrix(int i, int j, int val,
144
int delta, int offset) {
146
// map values in [1, size^2] --> [1, 255]
147
mat[i * size + j] = 1 + (254 * (val - 1)) / (size * size - 1);
149
buildDispersedMatrix(i, j,
150
val, delta / 2, 4*offset);
151
buildDispersedMatrix((i + delta) % size, (j + delta) % size,
152
val + offset, delta / 2, 4*offset);
153
buildDispersedMatrix((i + delta) % size, j,
154
val + 2*offset, delta / 2, 4*offset);
155
buildDispersedMatrix((i + 2*delta) % size, (j + delta) % size,
156
val + 3*offset, delta / 2, 4*offset);
160
void SplashScreen::buildClusteredMatrix() {
164
int size2, x, y, x1, y1, i;
168
// initialize the threshold matrix
169
for (y = 0; y < size; ++y) {
170
for (x = 0; x < size; ++x) {
171
mat[y * size + x] = 0;
175
// build the distance matrix
176
dist = (SplashCoord *)gmallocn(size * size2, sizeof(SplashCoord));
177
for (y = 0; y < size2; ++y) {
178
for (x = 0; x < size2; ++x) {
179
if (x + y < size2 - 1) {
180
u = (SplashCoord)x + 0.5 - 0;
181
v = (SplashCoord)y + 0.5 - 0;
183
u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
184
v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
186
dist[y * size2 + x] = u*u + v*v;
189
for (y = 0; y < size2; ++y) {
190
for (x = 0; x < size2; ++x) {
192
u = (SplashCoord)x + 0.5 - 0;
193
v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
195
u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
196
v = (SplashCoord)y + 0.5 - 0;
198
dist[(size2 + y) * size2 + x] = u*u + v*v;
202
// build the threshold matrix
205
x1 = y1 = 0; // make gcc happy
206
for (i = 0; i < size * size2; ++i) {
208
for (y = 0; y < size; ++y) {
209
for (x = 0; x < size2; ++x) {
210
if (mat[y * size + x] == 0 &&
211
dist[y * size2 + x] > d) {
214
d = dist[y1 * size2 + x1];
218
// map values in [0, 2*size*size2-1] --> [1, 255]
219
val = 1 + (254 * (2*i)) / (2*size*size2 - 1);
220
mat[y1 * size + x1] = val;
221
val = 1 + (254 * (2*i+1)) / (2*size*size2 - 1);
223
mat[(y1 + size2) * size + x1 + size2] = val;
225
mat[(y1 - size2) * size + x1 + size2] = val;
232
// Compute the distance between two points on a toroid.
233
int SplashScreen::distance(int x0, int y0, int x1, int y1) {
234
int dx0, dx1, dx, dy0, dy1, dy;
238
dx = dx0 < dx1 ? dx0 : dx1;
241
dy = dy0 < dy1 ? dy0 : dy1;
242
return dx * dx + dy * dy;
245
// Algorithm taken from:
246
// Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot
247
// Dithering" in Color Imaging: Device-Independent Color, Color
248
// Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999.
249
void SplashScreen::buildSCDMatrix(int r) {
250
SplashScreenPoint *dots, *pts;
251
int dotsLen, dotsSize;
255
int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n;
257
//~ this should probably happen somewhere else
260
// generate the random space-filling curve
261
pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint));
263
for (y = 0; y < size; ++y) {
264
for (x = 0; x < size; ++x) {
270
for (i = 0; i < size * size; ++i) {
271
j = i + (int)((double)(size * size - i) *
272
(double)rand() / ((double)RAND_MAX + 1.0));
281
// construct the circle template
282
tmpl = (char *)gmallocn((r+1)*(r+1), sizeof(char));
283
for (y = 0; y <= r; ++y) {
284
for (x = 0; x <= r; ++x) {
285
tmpl[y*(r+1) + x] = (x * y <= r * r) ? 1 : 0;
289
// mark all grid cells as free
290
grid = (char *)gmallocn(size * size, sizeof(char));
291
for (y = 0; y < size; ++y) {
292
for (x = 0; x < size; ++x) {
293
grid[y*size + x] = 0;
297
// walk the space-filling curve, adding dots
300
dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint));
301
for (i = 0; i < size * size; ++i) {
304
if (!grid[y*size + x]) {
305
if (dotsLen == dotsSize) {
307
dots = (SplashScreenPoint *)greallocn(dots, dotsSize,
308
sizeof(SplashScreenPoint));
310
dots[dotsLen++] = pts[i];
311
for (yy = 0; yy <= r; ++yy) {
312
y0 = (y + yy) % size;
313
y1 = (y - yy + size) % size;
314
for (xx = 0; xx <= r; ++xx) {
315
if (tmpl[yy*(r+1) + xx]) {
316
x0 = (x + xx) % size;
317
x1 = (x - xx + size) % size;
318
grid[y0*size + x0] = 1;
319
grid[y0*size + x1] = 1;
320
grid[y1*size + x0] = 1;
321
grid[y1*size + x1] = 1;
331
// assign each cell to a dot, compute distance to center of dot
332
region = (int *)gmallocn(size * size, sizeof(int));
333
dist = (int *)gmallocn(size * size, sizeof(int));
334
for (y = 0; y < size; ++y) {
335
for (x = 0; x < size; ++x) {
337
dMin = distance(dots[0].x, dots[0].y, x, y);
338
for (i = 1; i < dotsLen; ++i) {
339
d = distance(dots[i].x, dots[i].y, x, y);
345
region[y*size + x] = iMin;
346
dist[y*size + x] = dMin;
350
// compute threshold values
351
for (i = 0; i < dotsLen; ++i) {
353
for (y = 0; y < size; ++y) {
354
for (x = 0; x < size; ++x) {
355
if (region[y*size + x] == i) {
358
pts[n].dist = distance(dots[i].x, dots[i].y, x, y);
363
qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances);
364
for (j = 0; j < n; ++j) {
365
// map values in [0 .. n-1] --> [255 .. 1]
366
mat[pts[j].y * size + pts[j].x] = 255 - (254 * j) / (n - 1);
377
SplashScreen::SplashScreen(SplashScreen *screen) {
378
screenParams = screen->screenParams;
380
mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
381
memcpy(mat, screen->mat, size * size * sizeof(Guchar));
382
minVal = screen->minVal;
383
maxVal = screen->maxVal;
386
SplashScreen::~SplashScreen() {
390
int SplashScreen::test(int x, int y, Guchar value) {
393
if (mat == NULL) createMatrix();
395
if (value < minVal) {
398
if (value >= maxVal) {
401
if ((xx = x % size) < 0) {
404
if ((yy = y % size) < 0) {
407
return value < mat[yy * size + xx] ? 0 : 1;
410
GBool SplashScreen::isStatic(Guchar value) {
411
if (mat == NULL) createMatrix();
413
return value < minVal || value >= maxVal;