| 1 | gregl | 3.1 | /* Copyright (c) 1997 Silicon Graphics, Inc. */ | 
| 2 |  |  |  | 
| 3 |  |  | #ifndef lint | 
| 4 |  |  | static char SCCSid[] = "$SunId$ SGI"; | 
| 5 |  |  | #endif | 
| 6 |  |  |  | 
| 7 |  |  | /* | 
| 8 |  |  | * Quadtree driver support routines. | 
| 9 |  |  | */ | 
| 10 |  |  |  | 
| 11 |  |  | #include "standard.h" | 
| 12 |  |  | #include "rhd_qtree.h" | 
| 13 | gregl | 3.6 | /* quantity of leaves to free at a time */ | 
| 14 |  |  | #ifndef LFREEPCT | 
| 15 |  |  | #define LFREEPCT        25 | 
| 16 |  |  | #endif | 
| 17 | gregl | 3.1 |  | 
| 18 | gregl | 3.2 | RTREE   qtrunk;                 /* our quadtree trunk */ | 
| 19 |  |  | double  qtDepthEps = .02;       /* epsilon to compare depths (z fraction) */ | 
| 20 |  |  | int     qtMinNodesiz = 2;       /* minimum node dimension (pixels) */ | 
| 21 | gregl | 3.5 | struct rleaves  qtL;            /* our pile of leaves */ | 
| 22 | gregl | 3.2 |  | 
| 23 | gregl | 3.1 | #define TBUNDLESIZ      409     /* number of twigs in a bundle */ | 
| 24 |  |  |  | 
| 25 |  |  | static RTREE    **twigbundle;   /* free twig blocks (NULL term.) */ | 
| 26 |  |  | static int      nexttwig;       /* next free twig */ | 
| 27 |  |  |  | 
| 28 | gregl | 3.5 | #define is_stump(t)     (!((t)->flgs & (BR_ANY|LF_ANY))) | 
| 29 | gregl | 3.1 |  | 
| 30 |  |  |  | 
| 31 |  |  | static RTREE * | 
| 32 |  |  | newtwig()                       /* allocate a twig */ | 
| 33 |  |  | { | 
| 34 |  |  | register int    bi; | 
| 35 |  |  |  | 
| 36 |  |  | if (twigbundle == NULL) {       /* initialize */ | 
| 37 |  |  | twigbundle = (RTREE **)malloc(sizeof(RTREE *)); | 
| 38 |  |  | if (twigbundle == NULL) | 
| 39 |  |  | goto memerr; | 
| 40 |  |  | twigbundle[0] = NULL; | 
| 41 |  |  | } | 
| 42 |  |  | bi = nexttwig / TBUNDLESIZ; | 
| 43 |  |  | if (twigbundle[bi] == NULL) {   /* new block */ | 
| 44 |  |  | twigbundle = (RTREE **)realloc((char *)twigbundle, | 
| 45 |  |  | (bi+2)*sizeof(RTREE *)); | 
| 46 |  |  | if (twigbundle == NULL) | 
| 47 |  |  | goto memerr; | 
| 48 |  |  | twigbundle[bi] = (RTREE *)calloc(TBUNDLESIZ, sizeof(RTREE)); | 
| 49 |  |  | if (twigbundle[bi] == NULL) | 
| 50 |  |  | goto memerr; | 
| 51 |  |  | twigbundle[bi+1] = NULL; | 
| 52 |  |  | } | 
| 53 |  |  | /* nexttwig++ % TBUNDLESIZ */ | 
| 54 |  |  | return(twigbundle[bi] + (nexttwig++ - bi*TBUNDLESIZ)); | 
| 55 |  |  | memerr: | 
| 56 |  |  | error(SYSTEM, "out of memory in newtwig"); | 
| 57 |  |  | } | 
| 58 |  |  |  | 
| 59 |  |  |  | 
| 60 | gregl | 3.3 | qtFreeTree(really)              /* free allocated twigs */ | 
| 61 | gregl | 3.1 | int     really; | 
| 62 |  |  | { | 
| 63 |  |  | register int    i; | 
| 64 |  |  |  | 
| 65 | gregl | 3.7 | qtrunk.flgs = CH_ANY;   /* chop down tree */ | 
| 66 | gregl | 3.1 | if (twigbundle == NULL) | 
| 67 |  |  | return; | 
| 68 | gregl | 3.7 | i = (TBUNDLESIZ-1+nexttwig)/TBUNDLESIZ; | 
| 69 |  |  | nexttwig = 0; | 
| 70 | gregl | 3.1 | if (!really) {          /* just clear allocated blocks */ | 
| 71 | gregl | 3.7 | while (i--) | 
| 72 | gregl | 3.1 | bzero((char *)twigbundle[i], TBUNDLESIZ*sizeof(RTREE)); | 
| 73 |  |  | return; | 
| 74 |  |  | } | 
| 75 |  |  | /* else "really" means free up memory */ | 
| 76 |  |  | for (i = 0; twigbundle[i] != NULL; i++) | 
| 77 |  |  | free((char *)twigbundle[i]); | 
| 78 |  |  | free((char *)twigbundle); | 
| 79 |  |  | twigbundle = NULL; | 
| 80 |  |  | } | 
| 81 |  |  |  | 
| 82 |  |  |  | 
| 83 | gregl | 3.5 | static int | 
| 84 | gregl | 3.1 | newleaf()                       /* allocate a leaf from our pile */ | 
| 85 |  |  | { | 
| 86 | gregl | 3.5 | int     li; | 
| 87 | gregl | 3.4 |  | 
| 88 | gregl | 3.5 | li = qtL.tl++; | 
| 89 |  |  | if (qtL.tl >= qtL.nl)   /* get next leaf in ring */ | 
| 90 |  |  | qtL.tl = 0; | 
| 91 |  |  | if (qtL.tl == qtL.bl)   /* need to shake some free */ | 
| 92 | gregl | 3.1 | qtCompost(LFREEPCT); | 
| 93 | gregl | 3.5 | return(li); | 
| 94 | gregl | 3.1 | } | 
| 95 |  |  |  | 
| 96 |  |  |  | 
| 97 | gregl | 3.5 | #define LEAFSIZ         (3*sizeof(float)+sizeof(TMbright)+6*sizeof(BYTE)) | 
| 98 |  |  |  | 
| 99 | gregl | 3.1 | int | 
| 100 |  |  | qtAllocLeaves(n)                /* allocate space for n leaves */ | 
| 101 | gregl | 3.5 | register int    n; | 
| 102 | gregl | 3.1 | { | 
| 103 |  |  | unsigned        nbytes; | 
| 104 |  |  | register unsigned       i; | 
| 105 |  |  |  | 
| 106 | gregl | 3.3 | qtFreeTree(0);          /* make sure tree is empty */ | 
| 107 | gregl | 3.1 | if (n <= 0) | 
| 108 |  |  | return(0); | 
| 109 | gregl | 3.5 | if (qtL.nl >= n) | 
| 110 |  |  | return(qtL.nl); | 
| 111 |  |  | else if (qtL.nl > 0) | 
| 112 |  |  | free(qtL.base); | 
| 113 | gregl | 3.1 | /* round space up to nearest power of 2 */ | 
| 114 | gregl | 3.5 | nbytes = n*LEAFSIZ + 8; | 
| 115 | gregl | 3.1 | for (i = 1024; nbytes > i; i <<= 1) | 
| 116 |  |  | ; | 
| 117 | gregl | 3.5 | n = (i - 8) / LEAFSIZ;  /* should we make sure n is even? */ | 
| 118 |  |  | qtL.base = (char *)malloc(n*LEAFSIZ); | 
| 119 |  |  | if (qtL.base == NULL) | 
| 120 |  |  | return(0); | 
| 121 |  |  | /* assign larger alignment types earlier */ | 
| 122 |  |  | qtL.wp = (float (*)[3])qtL.base; | 
| 123 |  |  | qtL.brt = (TMbright *)(qtL.wp + n); | 
| 124 |  |  | qtL.chr = (BYTE (*)[3])(qtL.brt + n); | 
| 125 |  |  | qtL.rgb = (BYTE (*)[3])(qtL.chr + n); | 
| 126 |  |  | qtL.nl = n; | 
| 127 |  |  | qtL.tml = qtL.bl = qtL.tl = 0; | 
| 128 |  |  | return(n); | 
| 129 | gregl | 3.1 | } | 
| 130 |  |  |  | 
| 131 | gregl | 3.5 | #undef  LEAFSIZ | 
| 132 | gregl | 3.1 |  | 
| 133 | gregl | 3.5 |  | 
| 134 | gregl | 3.1 | qtFreeLeaves()                  /* free our allocated leaves and twigs */ | 
| 135 |  |  | { | 
| 136 | gregl | 3.3 | qtFreeTree(1);          /* free tree also */ | 
| 137 | gregl | 3.5 | if (qtL.nl <= 0) | 
| 138 | gregl | 3.1 | return; | 
| 139 | gregl | 3.5 | free(qtL.base); | 
| 140 |  |  | qtL.base = NULL; | 
| 141 |  |  | qtL.nl = 0; | 
| 142 | gregl | 3.1 | } | 
| 143 |  |  |  | 
| 144 |  |  |  | 
| 145 |  |  | static | 
| 146 |  |  | shaketree(tp)                   /* shake dead leaves from tree */ | 
| 147 |  |  | register RTREE  *tp; | 
| 148 |  |  | { | 
| 149 |  |  | register int    i, li; | 
| 150 |  |  |  | 
| 151 |  |  | for (i = 0; i < 4; i++) | 
| 152 | gregl | 3.5 | if (tp->flgs & BRF(i)) { | 
| 153 | gregl | 3.2 | shaketree(tp->k[i].b); | 
| 154 | gregl | 3.5 | if (is_stump(tp->k[i].b)) | 
| 155 |  |  | tp->flgs &= ~BRF(i); | 
| 156 |  |  | } else if (tp->flgs & LFF(i)) { | 
| 157 |  |  | li = tp->k[i].li; | 
| 158 |  |  | if (qtL.bl < qtL.tl ? | 
| 159 |  |  | (li < qtL.bl || li >= qtL.tl) : | 
| 160 |  |  | (li < qtL.bl && li >= qtL.tl)) | 
| 161 |  |  | tp->flgs &= ~LFF(i); | 
| 162 | gregl | 3.1 | } | 
| 163 |  |  | } | 
| 164 |  |  |  | 
| 165 |  |  |  | 
| 166 |  |  | int | 
| 167 |  |  | qtCompost(pct)                  /* free up some leaves */ | 
| 168 |  |  | int     pct; | 
| 169 |  |  | { | 
| 170 | gregl | 3.5 | int     nused, nclear, nmapped; | 
| 171 | gregl | 3.4 |  | 
| 172 | gregl | 3.1 | /* figure out how many leaves to clear */ | 
| 173 | gregl | 3.5 | nclear = qtL.nl * pct / 100; | 
| 174 |  |  | nused = qtL.tl - qtL.bl; | 
| 175 |  |  | if (nused <= 0) nused += qtL.nl; | 
| 176 |  |  | nclear -= qtL.nl - nused; | 
| 177 | gregl | 3.1 | if (nclear <= 0) | 
| 178 |  |  | return(0); | 
| 179 |  |  | if (nclear >= nused) {  /* clear them all */ | 
| 180 | gregl | 3.3 | qtFreeTree(0); | 
| 181 | gregl | 3.5 | qtL.tml = qtL.bl = qtL.tl = 0; | 
| 182 | gregl | 3.1 | return(nused); | 
| 183 |  |  | } | 
| 184 |  |  | /* else clear leaves from bottom */ | 
| 185 | gregl | 3.5 | nmapped = qtL.tml - qtL.bl; | 
| 186 |  |  | if (nmapped < 0) nmapped += qtL.nl; | 
| 187 |  |  | qtL.bl += nclear; | 
| 188 |  |  | if (qtL.bl >= qtL.nl) qtL.bl -= qtL.nl; | 
| 189 |  |  | if (nmapped <= nclear) qtL.tml = qtL.bl; | 
| 190 | gregl | 3.1 | shaketree(&qtrunk); | 
| 191 |  |  | return(nclear); | 
| 192 |  |  | } | 
| 193 |  |  |  | 
| 194 |  |  |  | 
| 195 | gregl | 3.5 | int | 
| 196 | gregl | 3.3 | qtFindLeaf(x, y)                /* find closest leaf to (x,y) */ | 
| 197 |  |  | int     x, y; | 
| 198 |  |  | { | 
| 199 |  |  | register RTREE  *tp = &qtrunk; | 
| 200 | gregl | 3.5 | int     li = -1; | 
| 201 | gregl | 3.3 | int     x0=0, y0=0, x1=odev.hres, y1=odev.vres; | 
| 202 |  |  | int     mx, my; | 
| 203 |  |  | register int    q; | 
| 204 |  |  | /* check limits */ | 
| 205 |  |  | if (x < 0 || x >= odev.hres || y < 0 || y >= odev.vres) | 
| 206 | gregl | 3.5 | return(-1); | 
| 207 | gregl | 3.3 | /* find nearby leaf in our tree */ | 
| 208 |  |  | for ( ; ; ) { | 
| 209 |  |  | for (q = 0; q < 4; q++)         /* find any leaf this level */ | 
| 210 | gregl | 3.5 | if (tp->flgs & LFF(q)) { | 
| 211 |  |  | li = tp->k[q].li; | 
| 212 | gregl | 3.3 | break; | 
| 213 |  |  | } | 
| 214 |  |  | q = 0;                          /* which quadrant are we? */ | 
| 215 |  |  | mx = (x0 + x1) >> 1; | 
| 216 |  |  | my = (y0 + y1) >> 1; | 
| 217 |  |  | if (x < mx) x1 = mx; | 
| 218 |  |  | else {x0 = mx; q |= 01;} | 
| 219 |  |  | if (y < my) y1 = my; | 
| 220 |  |  | else {y0 = my; q |= 02;} | 
| 221 |  |  | if (tp->flgs & BRF(q)) {        /* branch down if not a leaf */ | 
| 222 |  |  | tp = tp->k[q].b; | 
| 223 |  |  | continue; | 
| 224 |  |  | } | 
| 225 | gregl | 3.5 | if (tp->flgs & LFF(q))          /* good shot! */ | 
| 226 |  |  | return(tp->k[q].li); | 
| 227 |  |  | return(li);                     /* else return what we have */ | 
| 228 | gregl | 3.3 | } | 
| 229 |  |  | } | 
| 230 |  |  |  | 
| 231 |  |  |  | 
| 232 | gregl | 3.1 | static | 
| 233 | gregl | 3.5 | addleaf(li)                     /* add a leaf to our tree */ | 
| 234 |  |  | int     li; | 
| 235 | gregl | 3.1 | { | 
| 236 |  |  | register RTREE  *tp = &qtrunk; | 
| 237 |  |  | int     x0=0, y0=0, x1=odev.hres, y1=odev.vres; | 
| 238 | gregl | 3.5 | int     lo = -1; | 
| 239 | gregl | 3.1 | int     x, y, mx, my; | 
| 240 |  |  | double  z; | 
| 241 |  |  | FVECT   ip, wp; | 
| 242 |  |  | register int    q; | 
| 243 |  |  | /* compute leaf location */ | 
| 244 | gregl | 3.5 | VCOPY(wp, qtL.wp[li]); | 
| 245 | gregl | 3.1 | viewloc(ip, &odev.v, wp); | 
| 246 |  |  | if (ip[2] <= 0. || ip[0] < 0. || ip[0] >= 1. | 
| 247 |  |  | || ip[1] < 0. || ip[1] >= 1.) | 
| 248 |  |  | return; | 
| 249 |  |  | x = ip[0] * odev.hres; | 
| 250 |  |  | y = ip[1] * odev.vres; | 
| 251 |  |  | z = ip[2]; | 
| 252 |  |  | /* find the place for it */ | 
| 253 |  |  | for ( ; ; ) { | 
| 254 |  |  | q = 0;                          /* which quadrant? */ | 
| 255 |  |  | mx = (x0 + x1) >> 1; | 
| 256 |  |  | my = (y0 + y1) >> 1; | 
| 257 |  |  | if (x < mx) x1 = mx; | 
| 258 |  |  | else {x0 = mx; q |= 01;} | 
| 259 |  |  | if (y < my) y1 = my; | 
| 260 |  |  | else {y0 = my; q |= 02;} | 
| 261 |  |  | if (tp->flgs & BRF(q)) {        /* move to next branch */ | 
| 262 |  |  | tp->flgs |= CHF(q);             /* not sure; guess */ | 
| 263 |  |  | tp = tp->k[q].b; | 
| 264 |  |  | continue; | 
| 265 |  |  | } | 
| 266 | gregl | 3.5 | if (!(tp->flgs & LFF(q))) {     /* found stem for leaf */ | 
| 267 |  |  | tp->k[q].li = li; | 
| 268 |  |  | tp->flgs |= CHLFF(q); | 
| 269 | gregl | 3.1 | break; | 
| 270 |  |  | } | 
| 271 |  |  | /* check existing leaf */ | 
| 272 | gregl | 3.5 | if (lo != tp->k[q].li) { | 
| 273 |  |  | lo = tp->k[q].li; | 
| 274 |  |  | VCOPY(wp, qtL.wp[lo]); | 
| 275 | gregl | 3.1 | viewloc(ip, &odev.v, wp); | 
| 276 |  |  | } | 
| 277 |  |  | /* is node minimum size? */ | 
| 278 |  |  | if (x1-x0 <= qtMinNodesiz || y1-y0 <= qtMinNodesiz) { | 
| 279 |  |  | if (z > (1.-qtDepthEps)*ip[2])  /* who is closer? */ | 
| 280 |  |  | return;                 /* old one is */ | 
| 281 | gregl | 3.5 | tp->k[q].li = li;               /* new one is */ | 
| 282 | gregl | 3.1 | tp->flgs |= CHF(q); | 
| 283 |  |  | break; | 
| 284 |  |  | } | 
| 285 | gregl | 3.5 | tp->flgs &= ~LFF(q);            /* else grow tree */ | 
| 286 |  |  | tp->flgs |= CHBRF(q); | 
| 287 | gregl | 3.1 | tp = tp->k[q].b = newtwig(); | 
| 288 |  |  | q = 0;                          /* old leaf -> new branch */ | 
| 289 |  |  | mx = ip[0] * odev.hres; | 
| 290 |  |  | my = ip[1] * odev.vres; | 
| 291 |  |  | if (mx >= (x0 + x1) >> 1) q |= 01; | 
| 292 |  |  | if (my >= (y0 + y1) >> 1) q |= 02; | 
| 293 | gregl | 3.5 | tp->k[q].li = lo; | 
| 294 |  |  | tp->flgs |= LFF(q)|CH_ANY;      /* all new */ | 
| 295 | gregl | 3.1 | } | 
| 296 |  |  | } | 
| 297 |  |  |  | 
| 298 |  |  |  | 
| 299 |  |  | dev_value(c, p)                 /* add a pixel value to our output queue */ | 
| 300 |  |  | COLR    c; | 
| 301 |  |  | FVECT   p; | 
| 302 |  |  | { | 
| 303 | gregl | 3.5 | register int    li; | 
| 304 | gregl | 3.1 |  | 
| 305 | gregl | 3.5 | li = newleaf(); | 
| 306 |  |  | VCOPY(qtL.wp[li], p); | 
| 307 |  |  | tmCvColrs(&qtL.brt[li], qtL.chr[li], c, 1); | 
| 308 |  |  | addleaf(li); | 
| 309 | gregl | 3.1 | } | 
| 310 |  |  |  | 
| 311 |  |  |  | 
| 312 |  |  | qtReplant()                     /* replant our tree using new view */ | 
| 313 |  |  | { | 
| 314 |  |  | register int    i; | 
| 315 | gregl | 3.5 | /* anything to replant? */ | 
| 316 |  |  | if (qtL.bl == qtL.tl) | 
| 317 | gregl | 3.1 | return; | 
| 318 | gregl | 3.5 | qtFreeTree(0);                  /* blow the old tree away */ | 
| 319 |  |  | /* regrow it in new place */ | 
| 320 |  |  | for (i = qtL.bl; i != qtL.tl; ) { | 
| 321 |  |  | addleaf(i); | 
| 322 |  |  | if (++i >= qtL.nl) i = 0; | 
| 323 | gregl | 3.1 | } | 
| 324 |  |  | } | 
| 325 |  |  |  | 
| 326 |  |  |  | 
| 327 | gregl | 3.5 | qtMapLeaves(redo)               /* map our leaves to RGB */ | 
| 328 |  |  | int     redo; | 
| 329 |  |  | { | 
| 330 |  |  | int     aorg, alen, borg, blen; | 
| 331 | gregl | 3.6 | /* recompute mapping? */ | 
| 332 |  |  | if (redo) | 
| 333 |  |  | qtL.tml = qtL.bl; | 
| 334 | gregl | 3.5 | /* already done? */ | 
| 335 |  |  | if (qtL.tml == qtL.tl) | 
| 336 |  |  | return(1); | 
| 337 |  |  | /* compute segments */ | 
| 338 |  |  | aorg = qtL.tml; | 
| 339 |  |  | if (qtL.tl >= aorg) { | 
| 340 |  |  | alen = qtL.tl - aorg; | 
| 341 |  |  | blen = 0; | 
| 342 |  |  | } else { | 
| 343 |  |  | alen = qtL.nl - aorg; | 
| 344 |  |  | borg = 0; | 
| 345 |  |  | blen = qtL.tl; | 
| 346 |  |  | } | 
| 347 |  |  | /* (re)compute tone mapping? */ | 
| 348 |  |  | if (qtL.tml == qtL.bl) { | 
| 349 |  |  | tmClearHisto(); | 
| 350 |  |  | tmAddHisto(qtL.brt+aorg, alen, 1); | 
| 351 |  |  | if (blen > 0) | 
| 352 |  |  | tmAddHisto(qtL.brt+borg, blen, 1); | 
| 353 |  |  | if (tmComputeMapping(0., 0., 0.) != TM_E_OK) | 
| 354 |  |  | return(0); | 
| 355 |  |  | } | 
| 356 |  |  | if (tmMapPixels(qtL.rgb+aorg, qtL.brt+aorg, | 
| 357 |  |  | qtL.chr+aorg, alen) != TM_E_OK) | 
| 358 |  |  | return(0); | 
| 359 |  |  | if (blen > 0) | 
| 360 |  |  | tmMapPixels(qtL.rgb+borg, qtL.brt+borg, | 
| 361 |  |  | qtL.chr+borg, blen); | 
| 362 |  |  | qtL.tml = qtL.tl; | 
| 363 |  |  | return(1); | 
| 364 |  |  | } | 
| 365 |  |  |  | 
| 366 |  |  |  | 
| 367 | gregl | 3.1 | static | 
| 368 | gregl | 3.7 | redraw(tp, x0, y0, x1, y1, l)   /* mark portion of a tree for redraw */ | 
| 369 | gregl | 3.1 | register RTREE  *tp; | 
| 370 |  |  | int     x0, y0, x1, y1; | 
| 371 |  |  | int     l[2][2]; | 
| 372 |  |  | { | 
| 373 |  |  | int     quads = CH_ANY; | 
| 374 |  |  | int     mx, my; | 
| 375 |  |  | register int    i; | 
| 376 |  |  | /* compute midpoint */ | 
| 377 |  |  | mx = (x0 + x1) >> 1; | 
| 378 |  |  | my = (y0 + y1) >> 1; | 
| 379 |  |  | /* see what to do */ | 
| 380 |  |  | if (l[0][0] >= mx) | 
| 381 |  |  | quads &= ~(CHF(2)|CHF(0)); | 
| 382 | gregl | 3.7 | else if (l[0][1] < mx) | 
| 383 | gregl | 3.1 | quads &= ~(CHF(3)|CHF(1)); | 
| 384 |  |  | if (l[1][0] >= my) | 
| 385 |  |  | quads &= ~(CHF(1)|CHF(0)); | 
| 386 | gregl | 3.7 | else if (l[1][1] < my) | 
| 387 | gregl | 3.1 | quads &= ~(CHF(3)|CHF(2)); | 
| 388 | gregl | 3.7 | tp->flgs |= quads;              /* mark quadrants for update */ | 
| 389 |  |  | /* climb the branches */ | 
| 390 | gregl | 3.1 | for (i = 0; i < 4; i++) | 
| 391 | gregl | 3.7 | if (tp->flgs & BRF(i) && quads & CHF(i)) | 
| 392 |  |  | redraw(tp->k[i].b, i&01 ? mx : x0, i&02 ? my : y0, | 
| 393 | gregl | 3.1 | i&01 ? x1 : mx, i&02 ? y1 : my, l); | 
| 394 |  |  | } | 
| 395 |  |  |  | 
| 396 |  |  |  | 
| 397 |  |  | static | 
| 398 |  |  | update(ca, tp, x0, y0, x1, y1)  /* update tree display as needed */ | 
| 399 |  |  | BYTE    ca[3];          /* returned average color */ | 
| 400 |  |  | register RTREE  *tp; | 
| 401 |  |  | int     x0, y0, x1, y1; | 
| 402 |  |  | { | 
| 403 |  |  | int     csm[3], nc; | 
| 404 | gregl | 3.5 | register BYTE   *cp; | 
| 405 | gregl | 3.1 | BYTE    rgb[3]; | 
| 406 |  |  | int     gaps = 0; | 
| 407 |  |  | int     mx, my; | 
| 408 |  |  | register int    i; | 
| 409 |  |  | /* compute midpoint */ | 
| 410 |  |  | mx = (x0 + x1) >> 1; | 
| 411 |  |  | my = (y0 + y1) >> 1; | 
| 412 |  |  | csm[0] = csm[1] = csm[2] = nc = 0; | 
| 413 |  |  | /* do leaves first */ | 
| 414 | gregl | 3.5 | for (i = 0; i < 4; i++) { | 
| 415 |  |  | if (!(tp->flgs & CHF(i))) | 
| 416 |  |  | continue; | 
| 417 |  |  | if (tp->flgs & LFF(i)) { | 
| 418 |  |  | dev_paintr(cp=qtL.rgb[tp->k[i].li], | 
| 419 |  |  | i&01 ? mx : x0, i&02 ? my : y0, | 
| 420 | gregl | 3.1 | i&01 ? x1 : mx, i&02 ? y1 : my); | 
| 421 | gregl | 3.5 | csm[0] += cp[0]; csm[1] += cp[1]; csm[2] += cp[2]; | 
| 422 | gregl | 3.1 | nc++; | 
| 423 | gregl | 3.5 | } else if (!(tp->flgs & BRF(i))) | 
| 424 |  |  | gaps |= 1<<i;   /* empty stem */ | 
| 425 |  |  | } | 
| 426 | gregl | 3.1 | /* now do branches */ | 
| 427 |  |  | for (i = 0; i < 4; i++) | 
| 428 |  |  | if ((tp->flgs & CHBRF(i)) == CHBRF(i)) { | 
| 429 |  |  | update(rgb, tp->k[i].b, i&01 ? mx : x0, i&02 ? my : y0, | 
| 430 |  |  | i&01 ? x1 : mx, i&02 ? y1 : my); | 
| 431 |  |  | csm[0] += rgb[0]; csm[1] += rgb[1]; csm[2] += rgb[2]; | 
| 432 |  |  | nc++; | 
| 433 |  |  | } | 
| 434 |  |  | if (nc > 1) { | 
| 435 |  |  | ca[0] = csm[0]/nc; ca[1] = csm[1]/nc; ca[2] = csm[2]/nc; | 
| 436 |  |  | } else { | 
| 437 |  |  | ca[0] = csm[0]; ca[1] = csm[1]; ca[2] = csm[2]; | 
| 438 |  |  | } | 
| 439 |  |  | /* fill in gaps with average */ | 
| 440 |  |  | for (i = 0; gaps && i < 4; gaps >>= 1, i++) | 
| 441 |  |  | if (gaps & 01) | 
| 442 |  |  | dev_paintr(ca, i&01 ? mx : x0, i&02 ? my : y0, | 
| 443 |  |  | i&01 ? x1 : mx, i&02 ? y1 : my); | 
| 444 |  |  | tp->flgs &= ~CH_ANY;            /* all done */ | 
| 445 |  |  | } | 
| 446 |  |  |  | 
| 447 |  |  |  | 
| 448 | gregl | 3.5 | qtRedraw(x0, y0, x1, y1)        /* redraw part or all of our screen */ | 
| 449 | gregl | 3.1 | int     x0, y0, x1, y1; | 
| 450 |  |  | { | 
| 451 |  |  | int     lim[2][2]; | 
| 452 |  |  |  | 
| 453 |  |  | if (is_stump(&qtrunk)) | 
| 454 |  |  | return; | 
| 455 | gregl | 3.5 | if (!qtMapLeaves((lim[0][0]=x0) <= 0 & (lim[1][0]=y0) <= 0 & | 
| 456 |  |  | (lim[0][1]=x1) >= odev.hres-1 & (lim[1][1]=y1) >= odev.vres-1)) | 
| 457 |  |  | return; | 
| 458 | gregl | 3.7 | redraw(&qtrunk, 0, 0, odev.hres, odev.vres, lim); | 
| 459 | gregl | 3.1 | } | 
| 460 |  |  |  | 
| 461 |  |  |  | 
| 462 |  |  | qtUpdate()                      /* update our tree display */ | 
| 463 |  |  | { | 
| 464 |  |  | BYTE    ca[3]; | 
| 465 |  |  |  | 
| 466 |  |  | if (is_stump(&qtrunk)) | 
| 467 |  |  | return; | 
| 468 | gregl | 3.5 | if (!qtMapLeaves(0)) | 
| 469 |  |  | return; | 
| 470 | gregl | 3.1 | update(ca, &qtrunk, 0, 0, odev.hres, odev.vres); | 
| 471 |  |  | } |