void GetEncodedRGBAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int& red, unsigned int& green, unsigned int& blue, unsigned int& alpha, DitherHandler& dh, bool premul) { bool doPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round float fRed, fGreen, fBlue, fAlpha; img->GetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha); if (doPremultiply) { // Data has been stored premultiplied, but should be encoded non-premultiplied. // No need for special handling of color components greater than alpha. // Need to convert from premultiplied to non-premultiplied encoding. AlphaPremultiply(fRed, fGreen, fBlue, fAlpha); } else if (doUnPremultiply) { // Data has been stored premultiplied, but should be encoded non-premultiplied. // Clipping will happen /before/ re-multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects; // compensate for this by boosting opacity of any exceptionally bright pixels. float fBright = RGBColour(fRed, fGreen, fBlue).greyscale(); if (fBright > fAlpha) fAlpha = min(1.0f, fBright); // Need to convert from premultiplied to non-premultiplied encoding. AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha); } else if (!premul) { // Data has been stored un-premultiplied and should be encoded that way. // Clipping will happen /before/ multiplying with alpha (because the latter is done in the viewer), which is equivalent to clipping // pre-multiplied components to be no greater than alpha, thereby "killing" highlights on transparent objects; // compensate for this by boosting opacity of any exceptionally bright pixels. float fBright = RGBColour(fRed, fGreen, fBlue).greyscale(); if (fBright > 1.0) { float fFactor = fBright; if (fFactor * fAlpha > 1.0) fFactor = 1.0/fAlpha; // this keeps the product of alpha*color constant fAlpha *= fFactor; fRed /= fFactor; fGreen /= fFactor; fBlue /= fFactor; } // No need for converting between premultiplied and un-premultiplied encoding. } // else no need to worry about premultiplication DitherHandler::OffsetInfo linOff, encOff; dh.getOffset(x,y,linOff,encOff); red = IntEncode(g,fRed + linOff.red, max, encOff.red, linOff.red); green = IntEncode(g,fGreen + linOff.green, max, encOff.green, linOff.green); blue = IntEncode(g,fBlue + linOff.blue, max, encOff.blue, linOff.blue); alpha = IntEncode(fAlpha + linOff.alpha, max, encOff.alpha, linOff.alpha); dh.setError(x,y,linOff); }
void GetEncodedGrayAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float& fGray, float& fAlpha, bool premul) { bool doPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to apply premultiplication if encoded data should be premul'ed but container content isn't bool doUnPremultiply = !premul && img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round img->GetGrayAValue(x, y, fGray, fAlpha); if (doPremultiply) AlphaPremultiply(fGray, fAlpha); else if (doUnPremultiply) AlphaUnPremultiply(fGray, fAlpha); // else no need to worry about premultiplication fGray = GammaCurve::Encode(g,fGray); }
void SetEncodedRGBAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, float fRed, float fGreen, float fBlue, float fAlpha, bool premul) { bool doPremultiply = !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be bool doUnPremultiply = premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round fRed = GammaCurve::Decode(g,fRed); fGreen = GammaCurve::Decode(g,fGreen); fBlue = GammaCurve::Decode(g,fBlue); if (doPremultiply) AlphaPremultiply(fRed, fGreen, fBlue, fAlpha); else if (doUnPremultiply) AlphaUnPremultiply(fRed, fGreen, fBlue, fAlpha); // else no need to worry about premultiplication img->SetRGBAValue(x, y, fRed, fGreen, fBlue, fAlpha); }
void SetEncodedGrayAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int gray, unsigned int alpha, bool premul) { bool doPremultiply = (alpha != max) && !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be bool doUnPremultiply = (alpha != max) && premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round if (!doPremultiply && !doUnPremultiply && !img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g)) // avoid potential re-quantization in case we have a pretty match between encoded data and container img->SetGrayAValue(x, y, gray, alpha); else { float fAlpha = IntDecode(alpha,max); float fGray = IntDecode(g,gray,max); if (doPremultiply) AlphaPremultiply(fGray, fAlpha); else if (doUnPremultiply) AlphaUnPremultiply(fGray, fAlpha); // else no need to worry about premultiplication img->SetGrayAValue(x, y, fGray, fAlpha); } }
void AlphaUnPremultiply(RGBTColour& colour) { AlphaUnPremultiply(colour.red(), colour.green(), colour.blue(), colour.alpha()); }
void AlphaUnPremultiply(RGBFTColour& colour) { AlphaUnPremultiply(colour.red(), colour.green(), colour.blue(), colour.FTtoA()); }
void AlphaUnPremultiply(RGBColour& colour, float fAlpha) { AlphaUnPremultiply(colour.red(), colour.green(), colour.blue(), fAlpha); }
void image_colour_at(const ImageData *image, DBL xcoor, DBL ycoor, RGBFTColour& colour, int *index, bool premul) { *index = -1; bool doProperTransmitAll = image->data->HasTransparency() && !image->AllTransmitLegacyMode && !image->data->IsIndexed() && ( (image->AllTransmit != 0.0) || (image->AllFilter != 0.0) ); // If either source or destination uses premultiplied alpha, make sure interpolation is done in premultiplied space // as it makes the mathematical operations cleaner -- unless the alpha channel is modulated by "transmit all" or // "filter all", in which case we prefer non-premultiplied space. bool getPremul = doProperTransmitAll ? (premul && image->data->IsPremultiplied()) : (premul || image->data->IsPremultiplied()); switch(image->Interpolation_Type) { case NO_INTERPOLATION: no_interpolation(image, xcoor, ycoor, colour, index, getPremul); break; case BICUBIC: InterpolateBicubic(image, xcoor, ycoor, colour, index, getPremul); break; default: Interp(image, xcoor, ycoor, colour, index, getPremul); break; } bool havePremul = getPremul; if (!premul && havePremul) { // We fetched premultiplied data, but caller expects it non-premultiplied, so we need to fix that. // As "transmit/filter all" handling also works best on non-premultiplied data, we're doing this now. AlphaUnPremultiply(colour.rgb(), colour.FTtoA()); havePremul = false; } if (doProperTransmitAll) { COLC imageAlpha = colour.FTtoA(); if (imageAlpha != 0.0) // No need to apply "filter/transmit all" if the image is fully transparent here anyway. { colour.transm() += image->AllTransmit * imageAlpha; colour.filter() += image->AllFilter * imageAlpha; if (havePremul) { // We have premultiplied data here, and the caller expects it to stay that way (otherwise we'd already // have converted to non-premultiplied by now), so we need to fix the premultiplication of the colour // according to our modifications to the transparency components. COLC alphaCorrection = colour.FTtoA() / imageAlpha; AlphaPremultiply(colour.rgb(), alphaCorrection); } } } if (premul && !havePremul) { // We have non-premultiplied data here, but caller expects it premultiplied, so we need to fix that // As "transmit/filter all" handling works best on non-premultiplied data, we haven't done this earlier. AlphaPremultiply(colour.rgb(), colour.FTtoA()); } }