1 /** 2 Scanline conversion public API. This is used both internally and externally, because converting 3 a whole row of pixels at once is a rather common operations. 4 5 Copyright: Copyright Guillaume Piolat 2023 6 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 */ 8 module gamut.scanline; 9 10 11 import core.stdc.string: memcpy; 12 13 14 15 @system: 16 nothrow: 17 @nogc: 18 19 /// All scanlines conversion functions follow this signature: 20 /// inScan pointer to first pixel of input scanline 21 /// outScan pointer to first pixel of output scanline 22 /// 23 /// Type information, user data information, must be given by context. 24 /// Such a function assumes no overlap in memory between input and output scanlines. 25 alias scanlineConversionFunction_t = void function(const(ubyte)* inScan, ubyte* outScan, int width, void* userData); 26 27 28 29 // 30 // FROM xxxx TO rgb8 31 // (used in eg. the TGA encoder) 32 33 void scanline_convert_l8_to_rgb8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 34 { 35 ubyte* outb = outScan; 36 for (int x = 0; x < width; ++x) 37 { 38 ubyte b = inScan[x]; 39 *outb++ = b; 40 *outb++ = b; 41 *outb++ = b; 42 } 43 } 44 45 void scanline_convert_rgb8_to_rgb8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 46 { 47 memcpy(outScan, inScan, width * 3); 48 } 49 50 // 51 // FROM xxxx TO rgba8 52 // 53 54 void scanline_convert_l8_to_rgba8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 55 { 56 ubyte* outb = outScan; 57 for (int x = 0; x < width; ++x) 58 { 59 ubyte b = inScan[x]; 60 *outb++ = b; 61 *outb++ = b; 62 *outb++ = b; 63 *outb++ = 255; 64 } 65 } 66 void scanline_convert_la8_to_rgba8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 67 { 68 ubyte* outb = outScan; 69 for (int x = 0; x < width; ++x) 70 { 71 ubyte b = inScan[x*2]; 72 *outb++ = b; 73 *outb++ = b; 74 *outb++ = b; 75 *outb++ = inScan[x*2+1]; 76 } 77 } 78 void scanline_convert_rgb8_to_rgba8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 79 { 80 ubyte* outb = outScan; 81 for (int x = 0; x < width; ++x) 82 { 83 *outb++ = inScan[x*3+0]; 84 *outb++ = inScan[x*3+1]; 85 *outb++ = inScan[x*3+2]; 86 *outb++ = 255; 87 } 88 } 89 90 // 91 // FROM rgba8 TO xxxx 92 // 93 94 /// Convert a row of pixel from RGBA 8-bit (0 to 255) to L 8-bit (0 to 255). 95 void scanline_convert_rgba8_to_l8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 96 { 97 for (int x = 0; x < width; ++x) 98 { 99 outScan[x] = inScan[4*x]; 100 } 101 } 102 103 /// Convert a row of pixel from RGBA 8-bit (0 to 255) to LA 8-bit (0 to 255). 104 void scanline_convert_rgba8_to_la8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 105 { 106 for (int x = 0; x < width; ++x) 107 { 108 outScan[2*x+0] = inScan[4*x+0]; 109 outScan[2*x+1] = inScan[4*x+3]; 110 } 111 } 112 113 /// Convert a row of pixel from RGBA 8-bit (0 to 255) to RGB 8-bit (0 to 255). 114 void scanline_convert_rgba8_to_rgb8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 115 { 116 for (int x = 0; x < width; ++x) 117 { 118 outScan[3*x+0] = inScan[4*x+0]; 119 outScan[3*x+1] = inScan[4*x+1]; 120 outScan[3*x+2] = inScan[4*x+2]; 121 } 122 } 123 124 /// Convert a row of pixel from RGBA 8-bit (0 to 255) to RGBA 8-bit (0 to 255). 125 void scanline_convert_rgba8_to_rgba8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 126 { 127 memcpy(outScan, inScan, width * 4 * ubyte.sizeof); 128 } 129 130 // 131 // FROM xxxx TO rgbaf32 132 // 133 134 void scanline_convert_l8_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 135 { 136 const(ubyte)* s = inScan; 137 float* outp = cast(float*) outScan; 138 for (int x = 0; x < width; ++x) 139 { 140 float b = s[x] / 255.0f; 141 *outp++ = b; 142 *outp++ = b; 143 *outp++ = b; 144 *outp++ = 1.0f; 145 } 146 } 147 148 void scanline_convert_l16_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 149 { 150 const(ushort)* s = cast(const(ushort)*) inScan; 151 float* outp = cast(float*) outScan; 152 for (int x = 0; x < width; ++x) 153 { 154 float b = s[x] / 65535.0f; 155 *outp++ = b; 156 *outp++ = b; 157 *outp++ = b; 158 *outp++ = 1.0f; 159 } 160 } 161 162 void scanline_convert_lf32_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 163 { 164 const(float)* s = cast(const(float)*) inScan; 165 float* outp = cast(float*) outScan; 166 for (int x = 0; x < width; ++x) 167 { 168 float b = s[x]; 169 *outp++ = b; 170 *outp++ = b; 171 *outp++ = b; 172 *outp++ = 1.0f; 173 } 174 } 175 176 void scanline_convert_la8_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 177 { 178 const(ubyte)* s = inScan; 179 float* outp = cast(float*) outScan; 180 for (int x = 0; x < width; ++x) 181 { 182 float b = *s++ / 255.0f; 183 float a = *s++ / 255.0f; 184 *outp++ = b; 185 *outp++ = b; 186 *outp++ = b; 187 *outp++ = a; 188 } 189 } 190 191 void scanline_convert_la16_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 192 { 193 const(ushort)* s = cast(const(ushort)*) inScan; 194 float* outp = cast(float*) outScan; 195 for (int x = 0; x < width; ++x) 196 { 197 float b = *s++ / 65535.0f; 198 float a = *s++ / 65535.0f; 199 *outp++ = b; 200 *outp++ = b; 201 *outp++ = b; 202 *outp++ = a; 203 } 204 } 205 206 void scanline_convert_laf32_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 207 { 208 const(float)* s = cast(const(float)*) inScan; 209 float* outp = cast(float*) outScan; 210 for (int x = 0; x < width; ++x) 211 { 212 float b = *s++; 213 float a = *s++; 214 *outp++ = b; 215 *outp++ = b; 216 *outp++ = b; 217 *outp++ = a; 218 } 219 } 220 221 void scanline_convert_rgb8_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 222 { 223 const(ubyte)* s = inScan; 224 float* outp = cast(float*) outScan; 225 for (int x = 0; x < width; ++x) 226 { 227 float r = *s++ / 255.0f; 228 float g = *s++ / 255.0f; 229 float b = *s++ / 255.0f; 230 *outp++ = r; 231 *outp++ = g; 232 *outp++ = b; 233 *outp++ = 1.0f; 234 } 235 } 236 237 void scanline_convert_rgb16_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 238 { 239 const(ushort)* s = cast(const(ushort)*) inScan; 240 float* outp = cast(float*) outScan; 241 for (int x = 0; x < width; ++x) 242 { 243 float r = *s++ / 65535.0f; 244 float g = *s++ / 65535.0f; 245 float b = *s++ / 65535.0f; 246 *outp++ = r; 247 *outp++ = g; 248 *outp++ = b; 249 *outp++ = 1.0f; 250 } 251 } 252 253 void scanline_convert_rgbf32_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 254 { 255 const(float)* s = cast(const(float)*) inScan; 256 float* outp = cast(float*) outScan; 257 for (int x = 0; x < width; ++x) 258 { 259 float r = *s++; 260 float g = *s++; 261 float b = *s++; 262 *outp++ = r; 263 *outp++ = g; 264 *outp++ = b; 265 *outp++ = 1.0f; 266 } 267 } 268 269 void scanline_convert_rgba8_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 270 { 271 const(ubyte)* s = inScan; 272 float* outp = cast(float*) outScan; 273 for (int x = 0; x < width; ++x) 274 { 275 float r = *s++ / 255.0f; 276 float g = *s++ / 255.0f; 277 float b = *s++ / 255.0f; 278 float a = *s++ / 255.0f; 279 *outp++ = r; 280 *outp++ = g; 281 *outp++ = b; 282 *outp++ = a; 283 } 284 } 285 286 void scanline_convert_rgba16_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 287 { 288 const(ushort)* s = cast(const(ushort)*) inScan; 289 float* outp = cast(float*) outScan; 290 for (int x = 0; x < width; ++x) 291 { 292 float r = *s++ / 65535.0f; 293 float g = *s++ / 65535.0f; 294 float b = *s++ / 65535.0f; 295 float a = *s++ / 65535.0f; 296 *outp++ = r; 297 *outp++ = g; 298 *outp++ = b; 299 *outp++ = a; 300 } 301 } 302 303 // 304 // FROM rgbaf32 TO xxxx 305 // 306 307 /// Convert a row of pixel from RGBA 32-bit float (0 to 1.0) float to L 8-bit (0 to 255). 308 void scanline_convert_rgbaf32_to_l8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 309 { 310 const(float)* inp = cast(const(float)*)inScan; 311 ubyte* s = outScan; 312 for (int x = 0; x < width; ++x) 313 { 314 ubyte b = cast(ubyte)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 255.0f / 3.0f); 315 *s++ = b; 316 } 317 } 318 319 /// Convert a row of pixel from RGBA 32-bit float (0 to 1.0) float to L 16-bit (0 to 65535). 320 void scanline_convert_rgbaf32_to_l16(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 321 { 322 const(float)* inp = cast(const(float)*)inScan; 323 ushort* s = cast(ushort*) outScan; 324 for (int x = 0; x < width; ++x) 325 { 326 ushort b = cast(ushort)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 65535.0f / 3.0f); 327 *s++ = b; 328 } 329 } 330 331 332 /// Convert a row of pixel from RGBA 32-bit float (0 to 1.0) float to L 32-bit float (0 to 1.0). 333 void scanline_convert_rgbaf32_to_lf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 334 { 335 const(float)* inp = cast(const(float)*)inScan; 336 float* s = cast(float*) outScan; 337 for (int x = 0; x < width; ++x) 338 { 339 float b = (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) / 3.0f; 340 *s++ = b; 341 } 342 } 343 344 /// Convert a row of pixel from RGBA 32-bit float (0 to 1.0) to LA 16-bit (0 to 65535). 345 void scanline_convert_rgbaf32_to_la8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 346 { 347 // Issue #21, workaround for DMD optimizer. 348 version(DigitalMars) pragma(inline, false); 349 350 const(float)* inp = cast(const(float)*)inScan; 351 ubyte* s = outScan; 352 for (int x = 0; x < width; ++x) 353 { 354 ubyte b = cast(ubyte)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 255.0f / 3.0f); 355 ubyte a = cast(ubyte)(0.5f + inp[4*x+3] * 255.0f); 356 *s++ = b; 357 *s++ = a; 358 } 359 } 360 361 /// Convert a row of pixel from RGBA 32-bit float (0 to 1.0) to LA 16-bit (0 to 65535). 362 void scanline_convert_rgbaf32_to_la16(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 363 { 364 const(float)* inp = cast(const(float)*) inScan; 365 ushort* s = cast(ushort*) outScan; 366 for (int x = 0; x < width; ++x) 367 { 368 ushort b = cast(ushort)(0.5f + (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) * 65535.0f / 3.0f); 369 ushort a = cast(ushort)(0.5f + inp[4*x+3] * 65535.0f); 370 *s++ = b; 371 *s++ = a; 372 } 373 } 374 375 /// Convert a row of pixel from RGBA 32-bit float (0 to 1) to LA 32-bit float (0 to 1). 376 void scanline_convert_rgbaf32_to_laf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 377 { 378 const(float)* inp = cast(const(float)*) inScan; 379 float* s = cast(float*) outScan; 380 for (int x = 0; x < width; ++x) 381 { 382 float b = (inp[4*x+0] + inp[4*x+1] + inp[4*x+2]) / 3.0f; 383 float a = inp[4*x+3]; 384 *s++ = b; 385 *s++ = a; 386 } 387 } 388 389 /// Convert a row of pixel from RGBA 32-bit float (0 to 1) to RGB 8-bit (0 to 255). 390 void scanline_convert_rgbaf32_to_rgb8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 391 { 392 const(float)* inp = cast(const(float)*) inScan; 393 ubyte* s = outScan; 394 395 for (int x = 0; x < width; ++x) 396 { 397 ubyte r = cast(ubyte)(0.5f + inp[4*x+0] * 255.0f); 398 ubyte g = cast(ubyte)(0.5f + inp[4*x+1] * 255.0f); 399 ubyte b = cast(ubyte)(0.5f + inp[4*x+2] * 255.0f); 400 *s++ = r; 401 *s++ = g; 402 *s++ = b; 403 } 404 } 405 406 /// Convert a row of pixel from RGBA 32-bit float (0 to 1) to RGB 16-bit (0 to 65535). 407 void scanline_convert_rgbaf32_to_rgb16(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 408 { 409 const(float)* inp = cast(const(float)*) inScan; 410 ushort* s = cast(ushort*) outScan; 411 for (int x = 0; x < width; ++x) 412 { 413 ushort r = cast(ushort)(0.5f + inp[4*x+0] * 65535.0f); 414 ushort g = cast(ushort)(0.5f + inp[4*x+1] * 65535.0f); 415 ushort b = cast(ushort)(0.5f + inp[4*x+2] * 65535.0f); 416 *s++ = r; 417 *s++ = g; 418 *s++ = b; 419 } 420 } 421 422 /// Convert a row of pixel from RGBA 32-bit float (0 to 1) to RGB 16-bit (0 to 65535). 423 void scanline_convert_rgbaf32_to_rgbf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 424 { 425 const(float)* inp = cast(const(float)*) inScan; 426 float* s = cast(float*) outScan; 427 for (int x = 0; x < width; ++x) 428 { 429 *s++ = inp[4*x+0]; 430 *s++ = inp[4*x+1]; 431 *s++ = inp[4*x+2]; 432 } 433 } 434 435 /// Convert a row of pixel from RGBA 32-bit float (0 to 1) to RGBA 8-bit (0 to 255). 436 void scanline_convert_rgbaf32_to_rgba8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 437 { 438 const(float)* inp = cast(const(float)*) inScan; 439 ubyte* s = outScan; 440 for (int x = 0; x < width; ++x) 441 { 442 ubyte r = cast(ubyte)(0.5f + inp[4*x+0] * 255.0f); 443 ubyte g = cast(ubyte)(0.5f + inp[4*x+1] * 255.0f); 444 ubyte b = cast(ubyte)(0.5f + inp[4*x+2] * 255.0f); 445 ubyte a = cast(ubyte)(0.5f + inp[4*x+3] * 255.0f); 446 *s++ = r; 447 *s++ = g; 448 *s++ = b; 449 *s++ = a; 450 } 451 } 452 453 /// Convert a row of pixel from RGBA 32-bit float (0 to 1) to RGBA 16-bit (0 to 65535). 454 void scanline_convert_rgbaf32_to_rgba16(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 455 { 456 const(float)* inp = cast(const(float)*) inScan; 457 ushort* s = cast(ushort*)outScan; 458 for (int x = 0; x < width; ++x) 459 { 460 ushort r = cast(ushort)(0.5f + inp[4*x+0] * 65535.0f); 461 ushort g = cast(ushort)(0.5f + inp[4*x+1] * 65535.0f); 462 ushort b = cast(ushort)(0.5f + inp[4*x+2] * 65535.0f); 463 ushort a = cast(ushort)(0.5f + inp[4*x+3] * 65535.0f); 464 *s++ = r; 465 *s++ = g; 466 *s++ = b; 467 *s++ = a; 468 } 469 } 470 471 /// Convert a row of pixel from RGBA 32-bit float (0 to 1) to RGBA 32-bit float. 472 void scanline_convert_rgbaf32_to_rgbaf32(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 473 { 474 memcpy(outScan, inScan, width * 4 * float.sizeof); 475 } 476 477 478 // 479 // BMP ordering functions 480 // 481 482 /// Convert a row of pixel from RGBA 8-bit (0 to 255) to BGRA 8-bit (0 to 255). 483 void scanline_convert_rgba8_to_bgra8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 484 { 485 for (int x = 0; x < width; ++x) 486 { 487 outScan[4*x+0] = inScan[4*x+2]; 488 outScan[4*x+1] = inScan[4*x+1]; 489 outScan[4*x+2] = inScan[4*x+0]; 490 outScan[4*x+3] = inScan[4*x+3]; 491 } 492 } 493 ///ditto 494 alias scanline_convert_bgra8_to_rgba8 = scanline_convert_rgba8_to_bgra8; 495 496 /// Convert a row of pixel from RGB 8-bit (0 to 255) to BGR 8-bit (0 to 255). 497 void scanline_convert_rgb8_to_bgr8(const(ubyte)* inScan, ubyte* outScan, int width, void* userData = null) 498 { 499 for (int x = 0; x < width; ++x) 500 { 501 outScan[3*x+0] = inScan[3*x+2]; 502 outScan[3*x+1] = inScan[3*x+1]; 503 outScan[3*x+2] = inScan[3*x+0]; 504 } 505 } 506 ///ditto 507 alias scanline_convert_bgr8_to_rgb8 = scanline_convert_rgb8_to_bgr8;