the only exported function ...
| 347 | { |
| 348 | // the only exported function ... |
| 349 | bool writeJpeg(WRITE_ONE_BYTE output, const void* pixels_, unsigned short width, unsigned short height, |
| 350 | bool isRGB, unsigned char quality_, bool downsample, const char* comment) |
| 351 | { |
| 352 | // reject invalid pointers |
| 353 | if (output == nullptr || pixels_ == nullptr) |
| 354 | return false; |
| 355 | // check image format |
| 356 | if (width == 0 || height == 0) |
| 357 | return false; |
| 358 | |
| 359 | // number of components |
| 360 | const auto numComponents = isRGB ? 3 : 1; |
| 361 | // note: if there is just one component (=grayscale), then only luminance needs to be stored in the file |
| 362 | // thus everything related to chrominance need not to be written to the JPEG |
| 363 | // I still compute a few things, like quantization tables to avoid a complete code mess |
| 364 | |
| 365 | // grayscale images can't be downsampled (because there are no Cb + Cr channels) |
| 366 | if (!isRGB) |
| 367 | downsample = false; |
| 368 | |
| 369 | // wrapper for all output operations |
| 370 | BitWriter bitWriter(output); |
| 371 | |
| 372 | // //////////////////////////////////////// |
| 373 | // JFIF headers |
| 374 | const uint8_t HeaderJfif[2+2+16] = |
| 375 | { 0xFF,0xD8, // SOI marker (start of image) |
| 376 | 0xFF,0xE0, // JFIF APP0 tag |
| 377 | 0,16, // length: 16 bytes (14 bytes payload + 2 bytes for this length field) |
| 378 | 'J','F','I','F',0, // JFIF identifier, zero-terminated |
| 379 | 1,1, // JFIF version 1.1 |
| 380 | 0, // no density units specified |
| 381 | 0,1,0,1, // density: 1 pixel "per pixel" horizontally and vertically |
| 382 | 0,0 }; // no thumbnail (size 0 x 0) |
| 383 | bitWriter << HeaderJfif; |
| 384 | |
| 385 | // //////////////////////////////////////// |
| 386 | // comment (optional) |
| 387 | if (comment != nullptr) |
| 388 | { |
| 389 | // look for zero terminator |
| 390 | auto length = 0; // = strlen(comment); |
| 391 | while (comment[length] != 0) |
| 392 | length++; |
| 393 | |
| 394 | // write COM marker |
| 395 | bitWriter.addMarker(0xFE, 2+length); // block size is number of bytes (without zero terminator) + 2 bytes for this length field |
| 396 | // ... and write the comment itself |
| 397 | for (auto i = 0; i < length; i++) |
| 398 | bitWriter << comment[i]; |
| 399 | } |
| 400 | |
| 401 | // //////////////////////////////////////// |
| 402 | // adjust quantization tables to desired quality |
| 403 | |
| 404 | // quality level must be in 1 ... 100 |
| 405 | auto quality = clamp<uint16_t>(quality_, 1, 100); |
| 406 | // convert to an internal JPEG quality factor, formula taken from libjpeg |