mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 04:18:26 +01:00
fix TIFF encoder regarding rgbA/RGBA
Encoder: We were always using ExtraSamples=1, which means associated-alpha. But we don't need the (lossy) excursion to rgbA format. We can save the samples as RGBA directly, by changing ExtraSamples to '2'. The TIFF encoder now checks the colorspace properly, to handle premultiplied format as well as non-premultiplied. Decoder: The result of TIFFReadRGBAImageOriented() is always pre-multiply. So, in case an alpha channel is present, we need to unmultiply it before calling WebPPictureImportRGBA(). See: https://www.itu.int/itudoc/itu-t/com16/tiff-fx/docs/tiff6.pdf (page 31) and also http://www.asmail.be/msg0055469184.html Change-Id: I3258bfdb0eb2e1a53d6c04414f55edb2926c938c
This commit is contained in:
parent
17e3c11f99
commit
9273e441da
@ -332,9 +332,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
case BMP:
|
case BMP:
|
||||||
output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
|
output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
|
||||||
break;
|
break;
|
||||||
case TIFF: // note: force pre-multiplied alpha
|
case TIFF:
|
||||||
output_buffer->colorspace =
|
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
|
||||||
bitstream->has_alpha ? MODE_rgbA : MODE_RGB;
|
|
||||||
break;
|
break;
|
||||||
case PGM:
|
case PGM:
|
||||||
case RAW_YUV:
|
case RAW_YUV:
|
||||||
|
@ -361,6 +361,7 @@ int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
|
|||||||
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
const uint8_t* rgba = buffer->u.RGBA.rgba;
|
||||||
const int stride = buffer->u.RGBA.stride;
|
const int stride = buffer->u.RGBA.stride;
|
||||||
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
|
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
|
||||||
|
const int assoc_alpha = WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
|
||||||
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
||||||
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
|
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
|
||||||
: NUM_IFD_ENTRIES - 1;
|
: NUM_IFD_ENTRIES - 1;
|
||||||
@ -388,7 +389,8 @@ int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
|
|||||||
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
||||||
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
||||||
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
||||||
0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA
|
0x52, 0x01, 3, 0, 1, 0, 0, 0,
|
||||||
|
assoc_alpha, 0, 0, 0, // 178: ExtraSamples: rgbA/RGBA
|
||||||
0, 0, 0, 0, // 190: IFD terminator
|
0, 0, 0, 0, // 190: IFD terminator
|
||||||
// EXTRA_DATA_OFFSET:
|
// EXTRA_DATA_OFFSET:
|
||||||
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
||||||
|
@ -116,13 +116,48 @@ static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unmultiply Argb data. Taken from dsp/alpha_processing
|
||||||
|
// (we don't want to force a dependency to a libdspdec library).
|
||||||
|
#define MFIX 24 // 24bit fixed-point arithmetic
|
||||||
|
#define HALF ((1u << MFIX) >> 1)
|
||||||
|
#define KINV_255 ((1u << MFIX) / 255u)
|
||||||
|
|
||||||
|
static uint32_t Unmult(uint8_t x, uint32_t mult) {
|
||||||
|
const uint32_t v = (x * mult + HALF) >> MFIX;
|
||||||
|
return (v > 255u) ? 255u : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE uint32_t GetScale(uint32_t a) {
|
||||||
|
return (255u << MFIX) / a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MultARGBRow(uint8_t* ptr, int width) {
|
||||||
|
int x;
|
||||||
|
for (x = 0; x < width; ++x, ptr += 4) {
|
||||||
|
const uint32_t alpha = ptr[3];
|
||||||
|
if (alpha < 255) {
|
||||||
|
if (alpha == 0) { // alpha == 0
|
||||||
|
ptr[0] = ptr[1] = ptr[2] = 0;
|
||||||
|
} else {
|
||||||
|
const uint32_t scale = GetScale(alpha);
|
||||||
|
ptr[0] = Unmult(ptr[0], scale);
|
||||||
|
ptr[1] = Unmult(ptr[1], scale);
|
||||||
|
ptr[2] = Unmult(ptr[2], scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
||||||
WebPPicture* const pic, int keep_alpha,
|
WebPPicture* const pic, int keep_alpha,
|
||||||
Metadata* const metadata) {
|
Metadata* const metadata) {
|
||||||
MyData my_data = { data, (toff_t)data_size, 0 };
|
MyData my_data = { data, (toff_t)data_size, 0 };
|
||||||
TIFF* tif;
|
TIFF* tif;
|
||||||
uint32 width, height;
|
uint32_t width, height;
|
||||||
uint32* raster;
|
uint16_t samples_per_px = 0;
|
||||||
|
uint16_t extra_samples = 0;
|
||||||
|
uint16_t* extra_samples_ptr = NULL;
|
||||||
|
uint32_t* raster;
|
||||||
int64_t alloc_size;
|
int64_t alloc_size;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
tdir_t dircount;
|
tdir_t dircount;
|
||||||
@ -143,17 +178,27 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
|||||||
"Only the first will be used, %d will be ignored.\n",
|
"Only the first will be used, %d will be ignored.\n",
|
||||||
dircount - 1);
|
dircount - 1);
|
||||||
}
|
}
|
||||||
|
if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
|
||||||
|
fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
if (samples_per_px < 3 || samples_per_px > 4) goto End; // not supported
|
||||||
|
|
||||||
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
|
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
|
||||||
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
|
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
|
||||||
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
|
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height,
|
if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height,
|
||||||
sizeof(*raster))) {
|
sizeof(*raster))) {
|
||||||
goto End;
|
goto End;
|
||||||
}
|
}
|
||||||
|
if (!TIFFGetField(tif, TIFFTAG_EXTRASAMPLES,
|
||||||
|
&extra_samples, &extra_samples_ptr)) {
|
||||||
|
fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n");
|
||||||
|
goto End;
|
||||||
|
}
|
||||||
|
|
||||||
// _Tiffmalloc uses a signed type for size.
|
// _Tiffmalloc uses a signed type for size.
|
||||||
alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster));
|
alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster));
|
||||||
if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
|
if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
|
||||||
@ -169,6 +214,15 @@ int ReadTIFF(const uint8_t* const data, size_t data_size,
|
|||||||
#ifdef WORDS_BIGENDIAN
|
#ifdef WORDS_BIGENDIAN
|
||||||
TIFFSwabArrayOfLong(raster, width * height);
|
TIFFSwabArrayOfLong(raster, width * height);
|
||||||
#endif
|
#endif
|
||||||
|
// if we have an alpha channel, we must un-multiply from rgbA to RGBA
|
||||||
|
if (samples_per_px > 3 && extra_samples == 1) {
|
||||||
|
uint32_t y;
|
||||||
|
uint8_t* tmp = (uint8_t*)raster;
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
MultARGBRow(tmp, width);
|
||||||
|
tmp += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
ok = keep_alpha
|
ok = keep_alpha
|
||||||
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
|
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
|
||||||
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
|
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
|
||||||
|
Loading…
Reference in New Issue
Block a user