Austin Schuh | 3333ec7 | 2022-12-29 16:21:06 -0800 | [diff] [blame^] | 1 | /* Copyright (C) 2013-2016, The Regents of The University of Michigan. |
| 2 | All rights reserved. |
| 3 | This software was developed in the APRIL Robotics Lab under the |
| 4 | direction of Edwin Olson, ebolson@umich.edu. This software may be |
| 5 | available under alternative licensing terms; contact the address above. |
| 6 | Redistribution and use in source and binary forms, with or without |
| 7 | modification, are permitted provided that the following conditions are met: |
| 8 | 1. Redistributions of source code must retain the above copyright notice, this |
| 9 | list of conditions and the following disclaimer. |
| 10 | 2. Redistributions in binary form must reproduce the above copyright notice, |
| 11 | this list of conditions and the following disclaimer in the documentation |
| 12 | and/or other materials provided with the distribution. |
| 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
| 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 23 | The views and conclusions contained in the software and documentation are those |
| 24 | of the authors and should not be interpreted as representing official policies, |
| 25 | either expressed or implied, of the Regents of The University of Michigan. |
| 26 | */ |
| 27 | |
| 28 | #include <assert.h> |
| 29 | #include <stdio.h> |
| 30 | #include <stdlib.h> |
| 31 | #include <string.h> |
| 32 | #include <math.h> |
| 33 | |
| 34 | #include "common/image_u8.h" |
| 35 | #include "common/pnm.h" |
| 36 | #include "common/math_util.h" |
| 37 | |
| 38 | // least common multiple of 64 (sandy bridge cache line) and 24 (stride |
| 39 | // needed for RGB in 8-wide vector processing) |
| 40 | #define DEFAULT_ALIGNMENT_U8 96 |
| 41 | |
| 42 | image_u8_t *image_u8_create_stride(unsigned int width, unsigned int height, unsigned int stride) |
| 43 | { |
| 44 | uint8_t *buf = calloc(height*stride, sizeof(uint8_t)); |
| 45 | |
| 46 | // const initializer |
| 47 | image_u8_t tmp = { .width = width, .height = height, .stride = stride, .buf = buf }; |
| 48 | |
| 49 | image_u8_t *im = calloc(1, sizeof(image_u8_t)); |
| 50 | memcpy(im, &tmp, sizeof(image_u8_t)); |
| 51 | return im; |
| 52 | } |
| 53 | |
| 54 | image_u8_t *image_u8_create(unsigned int width, unsigned int height) |
| 55 | { |
| 56 | return image_u8_create_alignment(width, height, DEFAULT_ALIGNMENT_U8); |
| 57 | } |
| 58 | |
| 59 | image_u8_t *image_u8_create_alignment(unsigned int width, unsigned int height, unsigned int alignment) |
| 60 | { |
| 61 | int stride = width; |
| 62 | |
| 63 | if ((stride % alignment) != 0) |
| 64 | stride += alignment - (stride % alignment); |
| 65 | |
| 66 | return image_u8_create_stride(width, height, stride); |
| 67 | } |
| 68 | |
| 69 | image_u8_t *image_u8_copy(const image_u8_t *in) |
| 70 | { |
| 71 | uint8_t *buf = malloc(in->height*in->stride*sizeof(uint8_t)); |
| 72 | memcpy(buf, in->buf, in->height*in->stride*sizeof(uint8_t)); |
| 73 | |
| 74 | // const initializer |
| 75 | image_u8_t tmp = { .width = in->width, .height = in->height, .stride = in->stride, .buf = buf }; |
| 76 | |
| 77 | image_u8_t *copy = calloc(1, sizeof(image_u8_t)); |
| 78 | memcpy(copy, &tmp, sizeof(image_u8_t)); |
| 79 | return copy; |
| 80 | } |
| 81 | |
| 82 | void image_u8_destroy(image_u8_t *im) |
| 83 | { |
| 84 | if (!im) |
| 85 | return; |
| 86 | |
| 87 | free(im->buf); |
| 88 | free(im); |
| 89 | } |
| 90 | |
| 91 | //////////////////////////////////////////////////////////// |
| 92 | // PNM file i/o |
| 93 | image_u8_t *image_u8_create_from_pnm(const char *path) |
| 94 | { |
| 95 | return image_u8_create_from_pnm_alignment(path, DEFAULT_ALIGNMENT_U8); |
| 96 | } |
| 97 | |
| 98 | image_u8_t *image_u8_create_from_pnm_alignment(const char *path, int alignment) |
| 99 | { |
| 100 | pnm_t *pnm = pnm_create_from_file(path); |
| 101 | if (pnm == NULL) |
| 102 | return NULL; |
| 103 | |
| 104 | image_u8_t *im = NULL; |
| 105 | |
| 106 | switch (pnm->format) { |
| 107 | case PNM_FORMAT_GRAY: { |
| 108 | im = image_u8_create_alignment(pnm->width, pnm->height, alignment); |
| 109 | |
| 110 | if (pnm->max == 255) { |
| 111 | for (int y = 0; y < im->height; y++) |
| 112 | memcpy(&im->buf[y*im->stride], &pnm->buf[y*im->width], im->width); |
| 113 | } else if (pnm->max == 65535) { |
| 114 | for (int y = 0; y < im->height; y++) |
| 115 | for (int x = 0; x < im->width; x++) |
| 116 | im->buf[y*im->stride + x] = pnm->buf[2*(y*im->width + x)]; |
| 117 | } else { |
| 118 | assert(0); |
| 119 | } |
| 120 | |
| 121 | break; |
| 122 | } |
| 123 | |
| 124 | case PNM_FORMAT_RGB: { |
| 125 | im = image_u8_create_alignment(pnm->width, pnm->height, alignment); |
| 126 | |
| 127 | if (pnm->max == 255) { |
| 128 | // Gray conversion for RGB is gray = (r + g + g + b)/4 |
| 129 | for (int y = 0; y < im->height; y++) { |
| 130 | for (int x = 0; x < im->width; x++) { |
| 131 | uint8_t gray = (pnm->buf[y*im->width*3 + 3*x+0] + // r |
| 132 | pnm->buf[y*im->width*3 + 3*x+1] + // g |
| 133 | pnm->buf[y*im->width*3 + 3*x+1] + // g |
| 134 | pnm->buf[y*im->width*3 + 3*x+2]) // b |
| 135 | / 4; |
| 136 | |
| 137 | im->buf[y*im->stride + x] = gray; |
| 138 | } |
| 139 | } |
| 140 | } else if (pnm->max == 65535) { |
| 141 | for (int y = 0; y < im->height; y++) { |
| 142 | for (int x = 0; x < im->width; x++) { |
| 143 | int r = pnm->buf[6*(y*im->width + x) + 0]; |
| 144 | int g = pnm->buf[6*(y*im->width + x) + 2]; |
| 145 | int b = pnm->buf[6*(y*im->width + x) + 4]; |
| 146 | |
| 147 | im->buf[y*im->stride + x] = (r + g + g + b) / 4; |
| 148 | } |
| 149 | } |
| 150 | } else { |
| 151 | assert(0); |
| 152 | } |
| 153 | |
| 154 | break; |
| 155 | } |
| 156 | |
| 157 | case PNM_FORMAT_BINARY: { |
| 158 | im = image_u8_create_alignment(pnm->width, pnm->height, alignment); |
| 159 | |
| 160 | // image is padded to be whole bytes on each row. |
| 161 | |
| 162 | // how many bytes per row on the input? |
| 163 | int pbmstride = (im->width + 7) / 8; |
| 164 | |
| 165 | for (int y = 0; y < im->height; y++) { |
| 166 | for (int x = 0; x < im->width; x++) { |
| 167 | int byteidx = y * pbmstride + x / 8; |
| 168 | int bitidx = 7 - (x & 7); |
| 169 | |
| 170 | // ack, black is one according to pbm docs! |
| 171 | if ((pnm->buf[byteidx] >> bitidx) & 1) |
| 172 | im->buf[y*im->stride + x] = 0; |
| 173 | else |
| 174 | im->buf[y*im->stride + x] = 255; |
| 175 | } |
| 176 | } |
| 177 | break; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | pnm_destroy(pnm); |
| 182 | return im; |
| 183 | } |
| 184 | |
| 185 | image_u8_t *image_u8_create_from_f32(image_f32_t *fim) |
| 186 | { |
| 187 | image_u8_t *im = image_u8_create(fim->width, fim->height); |
| 188 | |
| 189 | for (int y = 0; y < fim->height; y++) { |
| 190 | for (int x = 0; x < fim->width; x++) { |
| 191 | float v = fim->buf[y*fim->stride + x]; |
| 192 | im->buf[y*im->stride + x] = (int) (255 * v); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | return im; |
| 197 | } |
| 198 | |
| 199 | |
| 200 | int image_u8_write_pnm(const image_u8_t *im, const char *path) |
| 201 | { |
| 202 | FILE *f = fopen(path, "wb"); |
| 203 | int res = 0; |
| 204 | |
| 205 | if (f == NULL) { |
| 206 | res = -1; |
| 207 | goto finish; |
| 208 | } |
| 209 | |
| 210 | // Only outputs to grayscale |
| 211 | fprintf(f, "P5\n%d %d\n255\n", im->width, im->height); |
| 212 | |
| 213 | for (int y = 0; y < im->height; y++) { |
| 214 | if (im->width != fwrite(&im->buf[y*im->stride], 1, im->width, f)) { |
| 215 | res = -2; |
| 216 | goto finish; |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | finish: |
| 221 | if (f != NULL) |
| 222 | fclose(f); |
| 223 | |
| 224 | return res; |
| 225 | } |
| 226 | |
| 227 | void image_u8_draw_circle(image_u8_t *im, float x0, float y0, float r, int v) |
| 228 | { |
| 229 | r = r*r; |
| 230 | |
| 231 | for (int y = y0-r; y <= y0+r; y++) { |
| 232 | for (int x = x0-r; x <= x0+r; x++) { |
| 233 | float d = (x-x0)*(x-x0) + (y-y0)*(y-y0); |
| 234 | if (d > r) |
| 235 | continue; |
| 236 | |
| 237 | if (x >= 0 && x < im->width && y >= 0 && y < im->height) { |
| 238 | int idx = y*im->stride + x; |
| 239 | im->buf[idx] = v; |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | void image_u8_draw_annulus(image_u8_t *im, float x0, float y0, float r0, float r1, int v) |
| 246 | { |
| 247 | r0 = r0*r0; |
| 248 | r1 = r1*r1; |
| 249 | |
| 250 | assert(r0 < r1); |
| 251 | |
| 252 | for (int y = y0-r1; y <= y0+r1; y++) { |
| 253 | for (int x = x0-r1; x <= x0+r1; x++) { |
| 254 | float d = (x-x0)*(x-x0) + (y-y0)*(y-y0); |
| 255 | if (d < r0 || d > r1) |
| 256 | continue; |
| 257 | |
| 258 | int idx = y*im->stride + x; |
| 259 | im->buf[idx] = v; |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | // only widths 1 and 3 supported (and 3 only badly) |
| 265 | void image_u8_draw_line(image_u8_t *im, float x0, float y0, float x1, float y1, int v, int width) |
| 266 | { |
| 267 | double dist = sqrtf((y1-y0)*(y1-y0) + (x1-x0)*(x1-x0)); |
| 268 | double delta = 0.5 / dist; |
| 269 | |
| 270 | // terrible line drawing code |
| 271 | for (float f = 0; f <= 1; f += delta) { |
| 272 | int x = ((int) (x1 + (x0 - x1) * f)); |
| 273 | int y = ((int) (y1 + (y0 - y1) * f)); |
| 274 | |
| 275 | if (x < 0 || y < 0 || x >= im->width || y >= im->height) |
| 276 | continue; |
| 277 | |
| 278 | int idx = y*im->stride + x; |
| 279 | im->buf[idx] = v; |
| 280 | if (width > 1) { |
| 281 | im->buf[idx+1] = v; |
| 282 | im->buf[idx+im->stride] = v; |
| 283 | im->buf[idx+1+im->stride] = v; |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | void image_u8_darken(image_u8_t *im) |
| 289 | { |
| 290 | for (int y = 0; y < im->height; y++) { |
| 291 | for (int x = 0; x < im->width; x++) { |
| 292 | im->buf[im->stride*y+x] /= 2; |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | static void convolve(const uint8_t *x, uint8_t *y, int sz, const uint8_t *k, int ksz) |
| 298 | { |
| 299 | assert((ksz&1)==1); |
| 300 | |
| 301 | for (int i = 0; i < ksz/2 && i < sz; i++) |
| 302 | y[i] = x[i]; |
| 303 | |
| 304 | for (int i = 0; i < sz - ksz; i++) { |
| 305 | uint32_t acc = 0; |
| 306 | |
| 307 | for (int j = 0; j < ksz; j++) |
| 308 | acc += k[j]*x[i+j]; |
| 309 | |
| 310 | y[ksz/2 + i] = acc >> 8; |
| 311 | } |
| 312 | |
| 313 | for (int i = sz - ksz + ksz/2; i < sz; i++) |
| 314 | y[i] = x[i]; |
| 315 | } |
| 316 | |
| 317 | void image_u8_convolve_2D(image_u8_t *im, const uint8_t *k, int ksz) |
| 318 | { |
| 319 | assert((ksz & 1) == 1); // ksz must be odd. |
| 320 | |
| 321 | for (int y = 0; y < im->height; y++) { |
| 322 | |
| 323 | uint8_t *x = malloc(sizeof(uint8_t)*im->stride); |
| 324 | memcpy(x, &im->buf[y*im->stride], im->stride); |
| 325 | |
| 326 | convolve(x, &im->buf[y*im->stride], im->width, k, ksz); |
| 327 | free(x); |
| 328 | } |
| 329 | |
| 330 | for (int x = 0; x < im->width; x++) { |
| 331 | uint8_t *xb = malloc(sizeof(uint8_t)*im->height); |
| 332 | uint8_t *yb = malloc(sizeof(uint8_t)*im->height); |
| 333 | |
| 334 | for (int y = 0; y < im->height; y++) |
| 335 | xb[y] = im->buf[y*im->stride + x]; |
| 336 | |
| 337 | convolve(xb, yb, im->height, k, ksz); |
| 338 | free(xb); |
| 339 | |
| 340 | for (int y = 0; y < im->height; y++) |
| 341 | im->buf[y*im->stride + x] = yb[y]; |
| 342 | free(yb); |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | void image_u8_gaussian_blur(image_u8_t *im, double sigma, int ksz) |
| 347 | { |
| 348 | if (sigma == 0) |
| 349 | return; |
| 350 | |
| 351 | assert((ksz & 1) == 1); // ksz must be odd. |
| 352 | |
| 353 | // build the kernel. |
| 354 | double *dk = malloc(sizeof(double)*ksz); |
| 355 | |
| 356 | // for kernel of length 5: |
| 357 | // dk[0] = f(-2), dk[1] = f(-1), dk[2] = f(0), dk[3] = f(1), dk[4] = f(2) |
| 358 | for (int i = 0; i < ksz; i++) { |
| 359 | int x = -ksz/2 + i; |
| 360 | double v = exp(-.5*sq(x / sigma)); |
| 361 | dk[i] = v; |
| 362 | } |
| 363 | |
| 364 | // normalize |
| 365 | double acc = 0; |
| 366 | for (int i = 0; i < ksz; i++) |
| 367 | acc += dk[i]; |
| 368 | |
| 369 | for (int i = 0; i < ksz; i++) |
| 370 | dk[i] /= acc; |
| 371 | |
| 372 | uint8_t *k = malloc(sizeof(uint8_t)*ksz); |
| 373 | for (int i = 0; i < ksz; i++) |
| 374 | k[i] = dk[i]*255; |
| 375 | |
| 376 | if (0) { |
| 377 | for (int i = 0; i < ksz; i++) |
| 378 | printf("%d %15f %5d\n", i, dk[i], k[i]); |
| 379 | } |
| 380 | free(dk); |
| 381 | |
| 382 | image_u8_convolve_2D(im, k, ksz); |
| 383 | free(k); |
| 384 | } |
| 385 | |
| 386 | image_u8_t *image_u8_rotate(const image_u8_t *in, double rad, uint8_t pad) |
| 387 | { |
| 388 | int iwidth = in->width, iheight = in->height; |
| 389 | rad = -rad; // interpret y as being "down" |
| 390 | |
| 391 | float c = cos(rad), s = sin(rad); |
| 392 | |
| 393 | float p[][2] = { { 0, 0}, { iwidth, 0 }, { iwidth, iheight }, { 0, iheight} }; |
| 394 | |
| 395 | float xmin = HUGE_VALF, xmax = -HUGE_VALF, ymin = HUGE_VALF, ymax = -HUGE_VALF; |
| 396 | float icx = iwidth / 2.0, icy = iheight / 2.0; |
| 397 | |
| 398 | for (int i = 0; i < 4; i++) { |
| 399 | float px = p[i][0] - icx; |
| 400 | float py = p[i][1] - icy; |
| 401 | |
| 402 | float nx = px*c - py*s; |
| 403 | float ny = px*s + py*c; |
| 404 | |
| 405 | xmin = fmin(xmin, nx); |
| 406 | xmax = fmax(xmax, nx); |
| 407 | ymin = fmin(ymin, ny); |
| 408 | ymax = fmax(ymax, ny); |
| 409 | } |
| 410 | |
| 411 | int owidth = ceil(xmax-xmin), oheight = ceil(ymax - ymin); |
| 412 | image_u8_t *out = image_u8_create(owidth, oheight); |
| 413 | |
| 414 | // iterate over output pixels. |
| 415 | for (int oy = 0; oy < oheight; oy++) { |
| 416 | for (int ox = 0; ox < owidth; ox++) { |
| 417 | // work backwards from destination coordinates... |
| 418 | // sample pixel centers. |
| 419 | float sx = ox - owidth / 2.0 + .5; |
| 420 | float sy = oy - oheight / 2.0 + .5; |
| 421 | |
| 422 | // project into input-image space |
| 423 | int ix = floor(sx*c + sy*s + icx); |
| 424 | int iy = floor(-sx*s + sy*c + icy); |
| 425 | |
| 426 | if (ix >= 0 && iy >= 0 && ix < iwidth && iy < iheight) |
| 427 | out->buf[oy*out->stride+ox] = in->buf[iy*in->stride + ix]; |
| 428 | else |
| 429 | out->buf[oy*out->stride+ox] = pad; |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | return out; |
| 434 | } |
| 435 | |
| 436 | image_u8_t *image_u8_decimate(image_u8_t *im, float ffactor) |
| 437 | { |
| 438 | int width = im->width, height = im->height; |
| 439 | |
| 440 | if (ffactor == 1.5) { |
| 441 | int swidth = width / 3 * 2, sheight = height / 3 * 2; |
| 442 | |
| 443 | image_u8_t *decim = image_u8_create(swidth, sheight); |
| 444 | |
| 445 | int y = 0, sy = 0; |
| 446 | while (sy < sheight) { |
| 447 | int x = 0, sx = 0; |
| 448 | while (sx < swidth) { |
| 449 | |
| 450 | // a b c |
| 451 | // d e f |
| 452 | // g h i |
| 453 | uint8_t a = im->buf[(y+0)*im->stride + (x+0)]; |
| 454 | uint8_t b = im->buf[(y+0)*im->stride + (x+1)]; |
| 455 | uint8_t c = im->buf[(y+0)*im->stride + (x+2)]; |
| 456 | |
| 457 | uint8_t d = im->buf[(y+1)*im->stride + (x+0)]; |
| 458 | uint8_t e = im->buf[(y+1)*im->stride + (x+1)]; |
| 459 | uint8_t f = im->buf[(y+1)*im->stride + (x+2)]; |
| 460 | |
| 461 | uint8_t g = im->buf[(y+2)*im->stride + (x+0)]; |
| 462 | uint8_t h = im->buf[(y+2)*im->stride + (x+1)]; |
| 463 | uint8_t i = im->buf[(y+2)*im->stride + (x+2)]; |
| 464 | |
| 465 | decim->buf[(sy+0)*decim->stride + (sx + 0)] = |
| 466 | (4*a+2*b+2*d+e)/9; |
| 467 | decim->buf[(sy+0)*decim->stride + (sx + 1)] = |
| 468 | (4*c+2*b+2*f+e)/9; |
| 469 | |
| 470 | decim->buf[(sy+1)*decim->stride + (sx + 0)] = |
| 471 | (4*g+2*d+2*h+e)/9; |
| 472 | decim->buf[(sy+1)*decim->stride + (sx + 1)] = |
| 473 | (4*i+2*f+2*h+e)/9; |
| 474 | |
| 475 | x += 3; |
| 476 | sx += 2; |
| 477 | } |
| 478 | |
| 479 | y += 3; |
| 480 | sy += 2; |
| 481 | } |
| 482 | |
| 483 | return decim; |
| 484 | } |
| 485 | |
| 486 | int factor = (int) ffactor; |
| 487 | |
| 488 | int swidth = 1 + (width - 1)/factor; |
| 489 | int sheight = 1 + (height - 1)/factor; |
| 490 | image_u8_t *decim = image_u8_create(swidth, sheight); |
| 491 | int sy = 0; |
| 492 | for (int y = 0; y < height; y += factor) { |
| 493 | int sx = 0; |
| 494 | for (int x = 0; x < width; x += factor) { |
| 495 | decim->buf[sy*decim->stride + sx] = im->buf[y*im->stride + x]; |
| 496 | sx++; |
| 497 | } |
| 498 | sy++; |
| 499 | } |
| 500 | return decim; |
| 501 | } |
| 502 | |
| 503 | void image_u8_fill_line_max(image_u8_t *im, const image_u8_lut_t *lut, const float *xy0, const float *xy1) |
| 504 | { |
| 505 | // what is the maximum distance that will result in drawing into our LUT? |
| 506 | float max_dist2 = (lut->nvalues-1)/lut->scale; |
| 507 | float max_dist = sqrt(max_dist2); |
| 508 | |
| 509 | // the orientation of the line |
| 510 | double theta = atan2(xy1[1]-xy0[1], xy1[0]-xy0[0]); |
| 511 | double v = sin(theta), u = cos(theta); |
| 512 | |
| 513 | int ix0 = iclamp(fmin(xy0[0], xy1[0]) - max_dist, 0, im->width-1); |
| 514 | int ix1 = iclamp(fmax(xy0[0], xy1[0]) + max_dist, 0, im->width-1); |
| 515 | |
| 516 | int iy0 = iclamp(fmin(xy0[1], xy1[1]) - max_dist, 0, im->height-1); |
| 517 | int iy1 = iclamp(fmax(xy0[1], xy1[1]) + max_dist, 0, im->height-1); |
| 518 | |
| 519 | // the line segment xy0---xy1 can be parameterized in terms of line coordinates. |
| 520 | // We fix xy0 to be at line coordinate 0. |
| 521 | float xy1_line_coord = (xy1[0]-xy0[0])*u + (xy1[1]-xy0[1])*v; |
| 522 | |
| 523 | float min_line_coord = fmin(0, xy1_line_coord); |
| 524 | float max_line_coord = fmax(0, xy1_line_coord); |
| 525 | |
| 526 | for (int iy = iy0; iy <= iy1; iy++) { |
| 527 | float y = iy+.5; |
| 528 | |
| 529 | for (int ix = ix0; ix <= ix1; ix++) { |
| 530 | float x = ix+.5; |
| 531 | |
| 532 | // compute line coordinate of this pixel. |
| 533 | float line_coord = (x - xy0[0])*u + (y - xy0[1])*v; |
| 534 | |
| 535 | // find point on line segment closest to our current pixel. |
| 536 | if (line_coord < min_line_coord) |
| 537 | line_coord = min_line_coord; |
| 538 | else if (line_coord > max_line_coord) |
| 539 | line_coord = max_line_coord; |
| 540 | |
| 541 | float px = xy0[0] + line_coord*u; |
| 542 | float py = xy0[1] + line_coord*v; |
| 543 | |
| 544 | double dist2 = (x-px)*(x-px) + (y-py)*(y-py); |
| 545 | |
| 546 | // not in our LUT? |
| 547 | int idx = dist2 * lut->scale; |
| 548 | if (idx >= lut->nvalues) |
| 549 | continue; |
| 550 | |
| 551 | uint8_t lut_value = lut->values[idx]; |
| 552 | uint8_t old_value = im->buf[iy*im->stride + ix]; |
| 553 | if (lut_value > old_value) |
| 554 | im->buf[iy*im->stride + ix] = lut_value; |
| 555 | } |
| 556 | } |
| 557 | } |