summaryrefslogtreecommitdiffstats
path: root/kpdf/xpdf/splash/SplashScreen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'kpdf/xpdf/splash/SplashScreen.cc')
-rw-r--r--kpdf/xpdf/splash/SplashScreen.cc385
1 files changed, 385 insertions, 0 deletions
diff --git a/kpdf/xpdf/splash/SplashScreen.cc b/kpdf/xpdf/splash/SplashScreen.cc
new file mode 100644
index 00000000..3e8d36ca
--- /dev/null
+++ b/kpdf/xpdf/splash/SplashScreen.cc
@@ -0,0 +1,385 @@
+//========================================================================
+//
+// SplashScreen.cc
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "SplashMath.h"
+#include "SplashScreen.h"
+
+//------------------------------------------------------------------------
+
+static SplashScreenParams defaultParams = {
+ splashScreenDispersed, // type
+ 2, // size
+ 2, // dotRadius
+ 1.0, // gamma
+ 0.0, // blackThreshold
+ 1.0 // whiteThreshold
+};
+
+//------------------------------------------------------------------------
+
+struct SplashScreenPoint {
+ int x, y;
+ int dist;
+};
+
+static int cmpDistances(const void *p0, const void *p1) {
+ return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist;
+}
+
+//------------------------------------------------------------------------
+// SplashScreen
+//------------------------------------------------------------------------
+
+// If <clustered> is true, this generates a 45 degree screen using a
+// circular dot spot function. DPI = resolution / ((size / 2) *
+// sqrt(2)). If <clustered> is false, this generates an optimal
+// threshold matrix using recursive tesselation. Gamma correction
+// (gamma = 1 / 1.33) is also computed here.
+SplashScreen::SplashScreen(SplashScreenParams *params) {
+ Guchar u, black, white;
+ int i;
+
+ if (!params) {
+ params = &defaultParams;
+ }
+
+ switch (params->type) {
+
+ case splashScreenDispersed:
+ // size must be a power of 2
+ for (size = 1; size < params->size; size <<= 1) ;
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ buildDispersedMatrix(size/2, size/2, 1, size/2, 1);
+ break;
+
+ case splashScreenClustered:
+ // size must be even
+ size = (params->size >> 1) << 1;
+ if (size < 2) {
+ size = 2;
+ }
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ buildClusteredMatrix();
+ break;
+
+ case splashScreenStochasticClustered:
+ // size must be at least 2*r
+ if (params->size < 2 * params->dotRadius) {
+ size = 2 * params->dotRadius;
+ } else {
+ size = params->size;
+ }
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ buildSCDMatrix(params->dotRadius);
+ break;
+ }
+
+ // do gamma correction and compute minVal/maxVal
+ minVal = 255;
+ maxVal = 0;
+ black = splashRound((SplashCoord)255.0 * params->blackThreshold);
+ if (black < 1) {
+ black = 1;
+ }
+ int whiteAux = splashRound((SplashCoord)255.0 * params->whiteThreshold);
+ if (whiteAux > 255) {
+ white = 255;
+ } else {
+ white = whiteAux;
+ }
+ for (i = 0; i < size * size; ++i) {
+ u = splashRound((SplashCoord)255.0 *
+ splashPow((SplashCoord)mat[i] / 255.0, params->gamma));
+ if (u < black) {
+ u = black;
+ } else if (u >= white) {
+ u = white;
+ }
+ mat[i] = u;
+ if (u < minVal) {
+ minVal = u;
+ } else if (u > maxVal) {
+ maxVal = u;
+ }
+ }
+}
+
+void SplashScreen::buildDispersedMatrix(int i, int j, int val,
+ int delta, int offset) {
+ if (delta == 0) {
+ // map values in [1, size^2] --> [1, 255]
+ mat[i * size + j] = 1 + (254 * (val - 1)) / (size * size - 1);
+ } else {
+ buildDispersedMatrix(i, j,
+ val, delta / 2, 4*offset);
+ buildDispersedMatrix((i + delta) % size, (j + delta) % size,
+ val + offset, delta / 2, 4*offset);
+ buildDispersedMatrix((i + delta) % size, j,
+ val + 2*offset, delta / 2, 4*offset);
+ buildDispersedMatrix((i + 2*delta) % size, (j + delta) % size,
+ val + 3*offset, delta / 2, 4*offset);
+ }
+}
+
+void SplashScreen::buildClusteredMatrix() {
+ SplashCoord *dist;
+ SplashCoord u, v, d;
+ Guchar val;
+ int size2, x, y, x1, y1, i;
+
+ size2 = size >> 1;
+
+ // initialize the threshold matrix
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ mat[y * size + x] = 0;
+ }
+ }
+
+ // build the distance matrix
+ dist = (SplashCoord *)gmallocn(size * size2, sizeof(SplashCoord));
+ for (y = 0; y < size2; ++y) {
+ for (x = 0; x < size2; ++x) {
+ if (x + y < size2 - 1) {
+ u = (SplashCoord)x + 0.5 - 0;
+ v = (SplashCoord)y + 0.5 - 0;
+ } else {
+ u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
+ v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
+ }
+ dist[y * size2 + x] = u*u + v*v;
+ }
+ }
+ for (y = 0; y < size2; ++y) {
+ for (x = 0; x < size2; ++x) {
+ if (x < y) {
+ u = (SplashCoord)x + 0.5 - 0;
+ v = (SplashCoord)y + 0.5 - (SplashCoord)size2;
+ } else {
+ u = (SplashCoord)x + 0.5 - (SplashCoord)size2;
+ v = (SplashCoord)y + 0.5 - 0;
+ }
+ dist[(size2 + y) * size2 + x] = u*u + v*v;
+ }
+ }
+
+ // build the threshold matrix
+ minVal = 1;
+ maxVal = 0;
+ x1 = y1 = 0; // make gcc happy
+ for (i = 0; i < size * size2; ++i) {
+ d = -1;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size2; ++x) {
+ if (mat[y * size + x] == 0 &&
+ dist[y * size2 + x] > d) {
+ x1 = x;
+ y1 = y;
+ d = dist[y1 * size2 + x1];
+ }
+ }
+ }
+ // map values in [0, 2*size*size2-1] --> [1, 255]
+ val = 1 + (254 * (2*i)) / (2*size*size2 - 1);
+ mat[y1 * size + x1] = val;
+ val = 1 + (254 * (2*i+1)) / (2*size*size2 - 1);
+ if (y1 < size2) {
+ mat[(y1 + size2) * size + x1 + size2] = val;
+ } else {
+ mat[(y1 - size2) * size + x1 + size2] = val;
+ }
+ }
+
+ gfree(dist);
+}
+
+// Compute the distance between two points on a toroid.
+int SplashScreen::distance(int x0, int y0, int x1, int y1) {
+ int dx0, dx1, dx, dy0, dy1, dy;
+
+ dx0 = abs(x0 - x1);
+ dx1 = size - dx0;
+ dx = dx0 < dx1 ? dx0 : dx1;
+ dy0 = abs(y0 - y1);
+ dy1 = size - dy0;
+ dy = dy0 < dy1 ? dy0 : dy1;
+ return dx * dx + dy * dy;
+}
+
+// Algorithm taken from:
+// Victor Ostromoukhov and Roger D. Hersch, "Stochastic Clustered-Dot
+// Dithering" in Color Imaging: Device-Independent Color, Color
+// Hardcopy, and Graphic Arts IV, SPIE Vol. 3648, pp. 496-505, 1999.
+void SplashScreen::buildSCDMatrix(int r) {
+ SplashScreenPoint *dots, *pts;
+ int dotsLen, dotsSize;
+ char *tmpl;
+ char *grid;
+ int *region, *dist;
+ int x, y, xx, yy, x0, x1, y0, y1, i, j, d, iMin, dMin, n;
+
+ //~ this should probably happen somewhere else
+ srand(123);
+
+ // generate the random space-filling curve
+ pts = (SplashScreenPoint *)gmallocn(size * size, sizeof(SplashScreenPoint));
+ i = 0;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ pts[i].x = x;
+ pts[i].y = y;
+ ++i;
+ }
+ }
+ for (i = 0; i < size * size; ++i) {
+ j = i + (int)((double)(size * size - i) *
+ (double)rand() / ((double)RAND_MAX + 1.0));
+ x = pts[i].x;
+ y = pts[i].y;
+ pts[i].x = pts[j].x;
+ pts[i].y = pts[j].y;
+ pts[j].x = x;
+ pts[j].y = y;
+ }
+
+ // construct the circle template
+ tmpl = (char *)gmallocn((r+1)*(r+1), sizeof(char));
+ for (y = 0; y <= r; ++y) {
+ for (x = 0; x <= r; ++x) {
+ tmpl[y*(r+1) + x] = (x * y <= r * r) ? 1 : 0;
+ }
+ }
+
+ // mark all grid cells as free
+ grid = (char *)gmallocn(size * size, sizeof(char));
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ grid[y*size + x] = 0;
+ }
+ }
+
+ // walk the space-filling curve, adding dots
+ dotsLen = 0;
+ dotsSize = 32;
+ dots = (SplashScreenPoint *)gmallocn(dotsSize, sizeof(SplashScreenPoint));
+ for (i = 0; i < size * size; ++i) {
+ x = pts[i].x;
+ y = pts[i].y;
+ if (!grid[y*size + x]) {
+ if (dotsLen == dotsSize) {
+ dotsSize *= 2;
+ dots = (SplashScreenPoint *)greallocn(dots, dotsSize,
+ sizeof(SplashScreenPoint));
+ }
+ dots[dotsLen++] = pts[i];
+ for (yy = 0; yy <= r; ++yy) {
+ y0 = (y + yy) % size;
+ y1 = (y - yy + size) % size;
+ for (xx = 0; xx <= r; ++xx) {
+ if (tmpl[yy*(r+1) + xx]) {
+ x0 = (x + xx) % size;
+ x1 = (x - xx + size) % size;
+ grid[y0*size + x0] = 1;
+ grid[y0*size + x1] = 1;
+ grid[y1*size + x0] = 1;
+ grid[y1*size + x1] = 1;
+ }
+ }
+ }
+ }
+ }
+
+ gfree(tmpl);
+ gfree(grid);
+
+ // assign each cell to a dot, compute distance to center of dot
+ region = (int *)gmallocn(size * size, sizeof(int));
+ dist = (int *)gmallocn(size * size, sizeof(int));
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ iMin = 0;
+ dMin = distance(dots[0].x, dots[0].y, x, y);
+ for (i = 1; i < dotsLen; ++i) {
+ d = distance(dots[i].x, dots[i].y, x, y);
+ if (d < dMin) {
+ iMin = i;
+ dMin = d;
+ }
+ }
+ region[y*size + x] = iMin;
+ dist[y*size + x] = dMin;
+ }
+ }
+
+ // compute threshold values
+ for (i = 0; i < dotsLen; ++i) {
+ n = 0;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ if (region[y*size + x] == i) {
+ pts[n].x = x;
+ pts[n].y = y;
+ pts[n].dist = distance(dots[i].x, dots[i].y, x, y);
+ ++n;
+ }
+ }
+ }
+ qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances);
+ for (j = 0; j < n; ++j) {
+ // map values in [0 .. n-1] --> [255 .. 1]
+ mat[pts[j].y * size + pts[j].x] = 255 - (254 * j) / (n - 1);
+ }
+ }
+
+ gfree(pts);
+ gfree(region);
+ gfree(dist);
+
+ gfree(dots);
+}
+
+SplashScreen::SplashScreen(SplashScreen *screen) {
+ size = screen->size;
+ mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));
+ memcpy(mat, screen->mat, size * size * sizeof(Guchar));
+ minVal = screen->minVal;
+ maxVal = screen->maxVal;
+}
+
+SplashScreen::~SplashScreen() {
+ gfree(mat);
+}
+
+int SplashScreen::test(int x, int y, Guchar value) {
+ int xx, yy;
+
+ if (value < minVal) {
+ return 0;
+ }
+ if (value >= maxVal) {
+ return 1;
+ }
+ if ((xx = x % size) < 0) {
+ xx = -xx;
+ }
+ if ((yy = y % size) < 0) {
+ yy = -yy;
+ }
+ return value < mat[yy * size + xx] ? 0 : 1;
+}
+
+GBool SplashScreen::isStatic(Guchar value) {
+ return value < minVal || value >= maxVal;
+}