1 //========================================================================
5 //========================================================================
10 #pragma implementation
16 #include "SplashMath.h"
17 #include "SplashScreen.h"
19 //------------------------------------------------------------------------
21 static SplashScreenParams defaultParams
= {
22 splashScreenDispersed
, // type
26 0.0, // blackThreshold
30 //------------------------------------------------------------------------
32 struct SplashScreenPoint
{
37 static int cmpDistances(const void *p0
, const void *p1
) {
38 return ((SplashScreenPoint
*)p0
)->dist
- ((SplashScreenPoint
*)p1
)->dist
;
41 //------------------------------------------------------------------------
43 //------------------------------------------------------------------------
45 // If <clustered> is true, this generates a 45 degree screen using a
46 // circular dot spot function. DPI = resolution / ((size / 2) *
47 // sqrt(2)). If <clustered> is false, this generates an optimal
48 // threshold matrix using recursive tesselation. Gamma correction
49 // (gamma = 1 / 1.33) is also computed here.
50 SplashScreen::SplashScreen(SplashScreenParams
*params
) {
51 Guchar u
, black
, white
;
55 params
= &defaultParams
;
58 switch (params
->type
) {
60 case splashScreenDispersed
:
61 // size must be a power of 2
62 for (size
= 1; size
< params
->size
; size
<<= 1) ;
63 mat
= (Guchar
*)gmallocn(size
* size
, sizeof(Guchar
));
64 buildDispersedMatrix(size
/2, size
/2, 1, size
/2, 1);
67 case splashScreenClustered
:
69 size
= (params
->size
>> 1) << 1;
73 mat
= (Guchar
*)gmallocn(size
* size
, sizeof(Guchar
));
74 buildClusteredMatrix();
77 case splashScreenStochasticClustered
:
78 // size must be at least 2*r
79 if (params
->size
< 2 * params
->dotRadius
) {
80 size
= 2 * params
->dotRadius
;
84 mat
= (Guchar
*)gmallocn(size
* size
, sizeof(Guchar
));
85 buildSCDMatrix(params
->dotRadius
);
89 // do gamma correction and compute minVal/maxVal
92 black
= splashRound((SplashCoord
)255.0 * params
->blackThreshold
);
96 white
= splashRound((SplashCoord
)255.0 * params
->whiteThreshold
);
97 //if (white > 255) { -- this never happens
100 for (i
= 0; i
< size
* size
; ++i
) {
101 u
= splashRound((SplashCoord
)255.0 *
102 splashPow((SplashCoord
)mat
[i
] / 255.0, params
->gamma
));
105 } else if (u
>= white
) {
111 } else if (u
> maxVal
) {
117 void SplashScreen::buildDispersedMatrix(int i
, int j
, int val
,
118 int delta
, int offset
) {
120 // map values in [1, size^2] --> [1, 255]
121 mat
[i
* size
+ j
] = 1 + (254 * (val
- 1)) / (size
* size
- 1);
123 buildDispersedMatrix(i
, j
,
124 val
, delta
/ 2, 4*offset
);
125 buildDispersedMatrix((i
+ delta
) % size
, (j
+ delta
) % size
,
126 val
+ offset
, delta
/ 2, 4*offset
);
127 buildDispersedMatrix((i
+ delta
) % size
, j
,
128 val
+ 2*offset
, delta
/ 2, 4*offset
);
129 buildDispersedMatrix((i
+ 2*delta
) % size
, (j
+ delta
) % size
,
130 val
+ 3*offset
, delta
/ 2, 4*offset
);
134 void SplashScreen::buildClusteredMatrix() {
138 int size2
, x
, y
, x1
, y1
, i
;
142 // initialize the threshold matrix
143 for (y
= 0; y
< size
; ++y
) {
144 for (x
= 0; x
< size
; ++x
) {
145 mat
[y
* size
+ x
] = 0;
149 // build the distance matrix
150 dist
= (SplashCoord
*)gmallocn(size
* size2
, sizeof(SplashCoord
));
151 for (y
= 0; y
< size2
; ++y
) {
152 for (x
= 0; x
< size2
; ++x
) {
153 if (x
+ y
< size2
- 1) {
154 u
= (SplashCoord
)x
+ 0.5 - 0;
155 v
= (SplashCoord
)y
+ 0.5 - 0;
157 u
= (SplashCoord
)x
+ 0.5 - (SplashCoord
)size2
;
158 v
= (SplashCoord
)y
+ 0.5 - (SplashCoord
)size2
;
160 dist
[y
* size2
+ x
] = u
*u
+ v
*v
;
163 for (y
= 0; y
< size2
; ++y
) {
164 for (x
= 0; x
< size2
; ++x
) {
166 u
= (SplashCoord
)x
+ 0.5 - 0;
167 v
= (SplashCoord
)y
+ 0.5 - (SplashCoord
)size2
;
169 u
= (SplashCoord
)x
+ 0.5 - (SplashCoord
)size2
;
170 v
= (SplashCoord
)y
+ 0.5 - 0;
172 dist
[(size2
+ y
) * size2
+ x
] = u
*u
+ v
*v
;
176 // build the threshold matrix
179 x1
= y1
= 0; // make gcc happy
180 for (i
= 0; i
< size
* size2
; ++i
) {
182 for (y
= 0; y
< size
; ++y
) {
183 for (x
= 0; x
< size2
; ++x
) {
184 if (mat
[y
* size
+ x
] == 0 &&
185 dist
[y
* size2
+ x
] > d
) {
188 d
= dist
[y1
* size2
+ x1
];
192 // map values in [0, 2*size*size2-1] --> [1, 255]
193 val
= 1 + (254 * (2*i
)) / (2*size
*size2
- 1);
194 mat
[y1
* size
+ x1
] = val
;
195 val
= 1 + (254 * (2*i
+1)) / (2*size
*size2
- 1);
197 mat
[(y1
+ size2
) * size
+ x1
+ size2
] = val
;
199 mat
[(y1
- size2
) * size
+ x1
+ size2
] = val
;
206 // Compute the distance between two points on a toroid.
207 int SplashScreen::distance(int x0
, int y0
, int x1
, int y1
) {
208 int dx0
, dx1
, dx
, dy0
, dy1
, dy
;
212 dx
= dx0
< dx1
? dx0
: dx1
;
215 dy
= dy0
< dy1
? dy0
: dy1
;
216 return dx
* dx
+ dy
* dy
;
219 // Algorithm taken from:
220 // Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot
221 // Dithering" in Color Imaging: Device-Independent Color, Color
222 // Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999.
223 void SplashScreen::buildSCDMatrix(int r
) {
224 SplashScreenPoint
*dots
, *pts
;
225 int dotsLen
, dotsSize
;
229 int x
, y
, xx
, yy
, x0
, x1
, y0
, y1
, i
, j
, d
, iMin
, dMin
, n
;
231 //~ this should probably happen somewhere else
234 // generate the random space-filling curve
235 pts
= (SplashScreenPoint
*)gmallocn(size
* size
, sizeof(SplashScreenPoint
));
237 for (y
= 0; y
< size
; ++y
) {
238 for (x
= 0; x
< size
; ++x
) {
244 for (i
= 0; i
< size
* size
; ++i
) {
245 j
= i
+ (int)((double)(size
* size
- i
) *
246 (double)rand() / ((double)RAND_MAX
+ 1.0));
255 // construct the circle template
256 tmpl
= (char *)gmallocn((r
+1)*(r
+1), sizeof(char));
257 for (y
= 0; y
<= r
; ++y
) {
258 for (x
= 0; x
<= r
; ++x
) {
259 tmpl
[y
*(r
+1) + x
] = (x
* y
<= r
* r
) ? 1 : 0;
263 // mark all grid cells as free
264 grid
= (char *)gmallocn(size
* size
, sizeof(char));
265 for (y
= 0; y
< size
; ++y
) {
266 for (x
= 0; x
< size
; ++x
) {
267 grid
[y
*size
+ x
] = 0;
271 // walk the space-filling curve, adding dots
274 dots
= (SplashScreenPoint
*)gmallocn(dotsSize
, sizeof(SplashScreenPoint
));
275 for (i
= 0; i
< size
* size
; ++i
) {
278 if (!grid
[y
*size
+ x
]) {
279 if (dotsLen
== dotsSize
) {
281 dots
= (SplashScreenPoint
*)greallocn(dots
, dotsSize
,
282 sizeof(SplashScreenPoint
));
284 dots
[dotsLen
++] = pts
[i
];
285 for (yy
= 0; yy
<= r
; ++yy
) {
286 y0
= (y
+ yy
) % size
;
287 y1
= (y
- yy
+ size
) % size
;
288 for (xx
= 0; xx
<= r
; ++xx
) {
289 if (tmpl
[yy
*(r
+1) + xx
]) {
290 x0
= (x
+ xx
) % size
;
291 x1
= (x
- xx
+ size
) % size
;
292 grid
[y0
*size
+ x0
] = 1;
293 grid
[y0
*size
+ x1
] = 1;
294 grid
[y1
*size
+ x0
] = 1;
295 grid
[y1
*size
+ x1
] = 1;
305 // assign each cell to a dot, compute distance to center of dot
306 region
= (int *)gmallocn(size
* size
, sizeof(int));
307 dist
= (int *)gmallocn(size
* size
, sizeof(int));
308 for (y
= 0; y
< size
; ++y
) {
309 for (x
= 0; x
< size
; ++x
) {
311 dMin
= distance(dots
[0].x
, dots
[0].y
, x
, y
);
312 for (i
= 1; i
< dotsLen
; ++i
) {
313 d
= distance(dots
[i
].x
, dots
[i
].y
, x
, y
);
319 region
[y
*size
+ x
] = iMin
;
320 dist
[y
*size
+ x
] = dMin
;
324 // compute threshold values
325 for (i
= 0; i
< dotsLen
; ++i
) {
327 for (y
= 0; y
< size
; ++y
) {
328 for (x
= 0; x
< size
; ++x
) {
329 if (region
[y
*size
+ x
] == i
) {
332 pts
[n
].dist
= distance(dots
[i
].x
, dots
[i
].y
, x
, y
);
337 qsort(pts
, n
, sizeof(SplashScreenPoint
), &cmpDistances
);
338 for (j
= 0; j
< n
; ++j
) {
339 // map values in [0 .. n-1] --> [255 .. 1]
340 mat
[pts
[j
].y
* size
+ pts
[j
].x
] = 255 - (254 * j
) / (n
- 1);
351 SplashScreen::SplashScreen(SplashScreen
*screen
) {
353 mat
= (Guchar
*)gmallocn(size
* size
, sizeof(Guchar
));
354 memcpy(mat
, screen
->mat
, size
* size
* sizeof(Guchar
));
355 minVal
= screen
->minVal
;
356 maxVal
= screen
->maxVal
;
359 SplashScreen::~SplashScreen() {
363 int SplashScreen::test(int x
, int y
, Guchar value
) {
366 if (value
< minVal
) {
369 if (value
>= maxVal
) {
372 if ((xx
= x
% size
) < 0) {
375 if ((yy
= y
% size
) < 0) {
378 return value
< mat
[yy
* size
+ xx
] ? 0 : 1;
381 GBool
SplashScreen::isStatic(Guchar value
) {
382 return value
< minVal
|| value
>= maxVal
;