static void Interp(const ImageData *image, DBL xcoor, DBL ycoor, RGBFTColour& colour, int *index, bool premul) { int iycoor, ixcoor, i; int Corners_Index[4]; RGBFTColour Corner_Colour[4]; DBL Corner_Factors[4]; xcoor += 0.5; ycoor += 0.5; iycoor = (int)ycoor; ixcoor = (int)xcoor; no_interpolation(image, (DBL)ixcoor, (DBL)iycoor, Corner_Colour[0], &Corners_Index[0], premul); no_interpolation(image, (DBL)ixcoor - 1, (DBL)iycoor, Corner_Colour[1], &Corners_Index[1], premul); no_interpolation(image, (DBL)ixcoor, (DBL)iycoor - 1, Corner_Colour[2], &Corners_Index[2], premul); no_interpolation(image, (DBL)ixcoor - 1, (DBL)iycoor - 1, Corner_Colour[3], &Corners_Index[3], premul); if(image->Interpolation_Type == BILINEAR) bilinear(Corner_Factors, xcoor, ycoor); else if(image->Interpolation_Type == NORMALIZED_DIST) norm_dist(Corner_Factors, xcoor, ycoor); else POV_ASSERT(false); // We're using double precision for the colors here to avoid higher-than-1.0 results due to rounding errors, // which would otherwise lead to stray dot artifacts when clamped to [0..1] range for a color_map or similar. // (Note that strictly speaking we don't avoid such rounding errors, but rather make them small enough that // subsequent rounding to single precision will take care of them.) PreciseRGBFTColour temp_colour; DBL temp_index = 0; for (i = 0; i < 4; i ++) { temp_colour += PreciseRGBFTColour(Corner_Colour[i]) * Corner_Factors[i]; temp_index += Corners_Index[i] * Corner_Factors[i]; } colour = RGBFTColour(temp_colour); *index = (int)temp_index; }
static void image_colour_at(IMAGE *Image, DBL xcoor, DBL ycoor, COLOUR colour, int *index) { switch (Image->Interpolation_Type) { case NO_INTERPOLATION: no_interpolation(Image, xcoor, ycoor, colour, index); break; default: Interp(Image, xcoor, ycoor, colour, index); break; } }
static void InterpolateBicubic(const ImageData *image, DBL xcoor, DBL ycoor, RGBFTColour& colour, int *index, bool premul) { int iycoor, ixcoor; int cornerIndex; RGBFTColour cornerColour; DBL factor; DBL factorsX[4]; DBL factorsY[4]; xcoor += 0.5; ycoor += 0.5; iycoor = (int)ycoor; ixcoor = (int)xcoor; cubic(factorsX, xcoor); cubic(factorsY, ycoor); // We're using double precision for the colors here to avoid higher-than-1.0 results due to rounding errors, // which would otherwise lead to stray dot artifacts when clamped to [0..1] range for a color_map or similar. // (Note that strictly speaking we don't avoid such rounding errors, but rather make them small enough that // subsequent rounding to single precision will take care of them.) // (Note that bicubic interpolation may still give values outside the range [0..1] at high-contrast edges; // this is an inherent property of this interpolation method, and is therefore accepted here.) PreciseRGBFTColour tempColour; DBL tempIndex = 0; for (int i = 0; i < 4; i ++) { for (int j = 0; j < 4; j ++) { cornerColour.Clear(); no_interpolation(image, (DBL)ixcoor + i-2, (DBL)iycoor + j-2, cornerColour, &cornerIndex, premul); factor = factorsX[i] * factorsY[j]; tempColour += PreciseRGBFTColour(cornerColour) * factor; tempIndex += cornerIndex * factor; } } colour = RGBFTColour(tempColour); *index = (int)tempIndex; }
static void Interp(IMAGE *Image, DBL xcoor, DBL ycoor, COLOUR colour, int *index) { int iycoor, ixcoor, i; int Corners_Index[4]; DBL Index_Crn[4]; COLOUR Corner_Colour[4]; DBL Red_Crn[4]; DBL Green_Crn[4]; DBL Blue_Crn[4]; DBL Filter_Crn[4]; DBL Transm_Crn[4]; DBL val1, val2, val3, val4, val5; val1 = val2 = val3 = val4 = val5 = 0.0; iycoor = (int)ycoor; ixcoor = (int)xcoor; for (i = 0; i < 4; i++) { Make_ColourA(Corner_Colour[i], 0.0, 0.0, 0.0, 0.0, 0.0); } /* OK, now that you have the corners, what are you going to do with them? */ if (Image->Interpolation_Type == BILINEAR) { no_interpolation(Image, (DBL)ixcoor + 1, (DBL)iycoor, Corner_Colour[0], &Corners_Index[0]); no_interpolation(Image, (DBL)ixcoor, (DBL)iycoor, Corner_Colour[1], &Corners_Index[1]); no_interpolation(Image, (DBL)ixcoor + 1, (DBL)iycoor - 1, Corner_Colour[2], &Corners_Index[2]); no_interpolation(Image, (DBL)ixcoor, (DBL)iycoor - 1, Corner_Colour[3], &Corners_Index[3]); for (i = 0; i < 4; i++) { Red_Crn[i] = Corner_Colour[i][pRED]; Green_Crn[i] = Corner_Colour[i][pGREEN]; Blue_Crn[i] = Corner_Colour[i][pBLUE]; Filter_Crn[i] = Corner_Colour[i][pFILTER]; Transm_Crn[i] = Corner_Colour[i][pTRANSM]; // Debug_Info("Crn %d = %lf %lf %lf\n",i,Red_Crn[i],Blue_Crn[i],Green_Crn[i]); } val1 = bilinear(Red_Crn, xcoor, ycoor); val2 = bilinear(Green_Crn, xcoor, ycoor); val3 = bilinear(Blue_Crn, xcoor, ycoor); val4 = bilinear(Filter_Crn, xcoor, ycoor); val5 = bilinear(Transm_Crn, xcoor, ycoor); } if (Image->Interpolation_Type == NORMALIZED_DIST) { no_interpolation(Image, (DBL)ixcoor, (DBL)iycoor - 1, Corner_Colour[0], &Corners_Index[0]); no_interpolation(Image, (DBL)ixcoor + 1, (DBL)iycoor - 1, Corner_Colour[1], &Corners_Index[1]); no_interpolation(Image, (DBL)ixcoor, (DBL)iycoor, Corner_Colour[2], &Corners_Index[2]); no_interpolation(Image, (DBL)ixcoor + 1, (DBL)iycoor, Corner_Colour[3], &Corners_Index[3]); for (i = 0; i < 4; i++) { Red_Crn[i] = Corner_Colour[i][pRED]; Green_Crn[i] = Corner_Colour[i][pGREEN]; Blue_Crn[i] = Corner_Colour[i][pBLUE]; Filter_Crn[i] = Corner_Colour[i][pFILTER]; Transm_Crn[i] = Corner_Colour[i][pTRANSM]; // Debug_Info("Crn %d = %lf %lf %lf\n",i,Red_Crn[i],Blue_Crn[i],Green_Crn[i]); } val1 = norm_dist(Red_Crn, xcoor, ycoor); val2 = norm_dist(Green_Crn, xcoor, ycoor); val3 = norm_dist(Blue_Crn, xcoor, ycoor); val4 = norm_dist(Filter_Crn, xcoor, ycoor); val5 = norm_dist(Transm_Crn, xcoor, ycoor); } colour[pRED] += val1; colour[pGREEN] += val2; colour[pBLUE] += val3; colour[pFILTER] += val4; colour[pTRANSM] += val5; // Debug_Info("Final = %lf %lf %lf\n",val1,val2,val3); // use bilinear for index try average later for (i = 0; i < 4; i++) { Index_Crn[i] = (DBL)Corners_Index[i]; } if (Image->Interpolation_Type == BILINEAR) { *index = (int)(bilinear(Index_Crn, xcoor, ycoor) + 0.5); } if (Image->Interpolation_Type == NORMALIZED_DIST) { *index = (int)(norm_dist(Index_Crn, xcoor, ycoor) + 0.5); } }
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()); } }