MINLINE void linearrgb_to_srgb_predivide_v4(float srgb[4], const float linear[4]) { float alpha, inv_alpha; if (linear[3] == 1.0f || linear[3] == 0.0f) { alpha = 1.0f; inv_alpha = 1.0f; } else { alpha = linear[3]; inv_alpha = 1.0f / alpha; } srgb[0] = linearrgb_to_srgb(linear[0] * inv_alpha) * alpha; srgb[1] = linearrgb_to_srgb(linear[1] * inv_alpha) * alpha; srgb[2] = linearrgb_to_srgb(linear[2] * inv_alpha) * alpha; srgb[3] = linear[3]; }
void floatbuf_to_srgb_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int UNUSED(w)) { int x, y; float *rf= rectf; float srgb[3]; unsigned char *rc= rectc; for(y=y1; y<y2; y++) { for(x=x1; x<x2; x++, rf+=4, rc+=4) { srgb[0]= linearrgb_to_srgb(rf[0]); srgb[1]= linearrgb_to_srgb(rf[1]); srgb[2]= linearrgb_to_srgb(rf[2]); rc[0]= FTOCHAR(srgb[0]); rc[1]= FTOCHAR(srgb[1]); rc[2]= FTOCHAR(srgb[2]); rc[3]= FTOCHAR(rf[3]); } } }
/* note: lift_lgg is just 2-lift, gamma_inv is 1.0/gamma */ DO_INLINE float colorbalance_lgg(float in, float lift_lgg, float gamma_inv, float gain) { /* 1:1 match with the sequencer with linear/srgb conversions, the conversion isnt pretty * but best keep it this way, sice testing for durian shows a similar calculation * without lin/srgb conversions gives bad results (over-saturated shadows) with colors * slightly below 1.0. some correction can be done but it ends up looking bad for shadows or lighter tones - campbell */ float x= (((linearrgb_to_srgb(in) - 1.0f) * lift_lgg) + 1.0f) * gain; /* prevent NaN */ if (x < 0.f) x = 0.f; return powf(srgb_to_linearrgb(x), gamma_inv); }
void IMB_convert_profile(struct ImBuf *ibuf, int profile) { int ok= FALSE; int i; unsigned char *rct= (unsigned char *)ibuf->rect; float *rctf= ibuf->rect_float; if(ibuf->profile == profile) return; if(ELEM(ibuf->profile, IB_PROFILE_NONE, IB_PROFILE_SRGB)) { /* from */ if(profile == IB_PROFILE_LINEAR_RGB) { /* to */ if(ibuf->rect_float) { for (i = ibuf->x * ibuf->y; i > 0; i--, rctf+=4) { rctf[0]= srgb_to_linearrgb(rctf[0]); rctf[1]= srgb_to_linearrgb(rctf[1]); rctf[2]= srgb_to_linearrgb(rctf[2]); } } if(ibuf->rect) { for (i = ibuf->x * ibuf->y; i > 0; i--, rct+=4) { rct[0]= (unsigned char)((srgb_to_linearrgb((float)rct[0]/255.0f) * 255.0f) + 0.5f); rct[1]= (unsigned char)((srgb_to_linearrgb((float)rct[1]/255.0f) * 255.0f) + 0.5f); rct[2]= (unsigned char)((srgb_to_linearrgb((float)rct[2]/255.0f) * 255.0f) + 0.5f); } } ok= TRUE; } } else if (ibuf->profile == IB_PROFILE_LINEAR_RGB) { /* from */ if(ELEM(profile, IB_PROFILE_NONE, IB_PROFILE_SRGB)) { /* to */ if(ibuf->rect_float) { for (i = ibuf->x * ibuf->y; i > 0; i--, rctf+=4) { rctf[0]= linearrgb_to_srgb(rctf[0]); rctf[1]= linearrgb_to_srgb(rctf[1]); rctf[2]= linearrgb_to_srgb(rctf[2]); } } if(ibuf->rect) { for (i = ibuf->x * ibuf->y; i > 0; i--, rct+=4) { rct[0]= (unsigned char)((linearrgb_to_srgb((float)rct[0]/255.0f) * 255.0f) + 0.5f); rct[1]= (unsigned char)((linearrgb_to_srgb((float)rct[1]/255.0f) * 255.0f) + 0.5f); rct[2]= (unsigned char)((linearrgb_to_srgb((float)rct[2]/255.0f) * 255.0f) + 0.5f); } } ok= TRUE; } } if(ok==FALSE){ printf("IMB_convert_profile: failed profile conversion %d -> %d\n", ibuf->profile, profile); return; } ibuf->profile= profile; }
MINLINE void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3]) { srgb[0] = linearrgb_to_srgb(linear[0]); srgb[1] = linearrgb_to_srgb(linear[1]); srgb[2] = linearrgb_to_srgb(linear[2]); }
static void fill_bins(bNode* node, CompBuf* in, int* bins, int colorcor) { float value[4]; int ivalue=0; int x,y; /*fill bins */ for(y=0; y<in->y; y++) { for(x=0; x<in->x; x++) { /* get the pixel */ qd_getPixel(in, x, y, value); if(value[3] > 0.0) { /* don't count transparent pixels */ switch(node->custom1) { case 1: { /* all colors */ if(colorcor) linearrgb_to_srgb_v3_v3(&value[0],&value[0]); rgb_tobw(value[0],value[1],value[2], &value[0]); value[0]=value[0]*255; /* scale to 0-255 range */ ivalue=(int)value[0]; break; } case 2: { /* red channel */ if(colorcor) value[0]=linearrgb_to_srgb(value[0]); value[0]=value[0]*255; /* scale to 0-255 range */ ivalue=(int)value[0]; break; } case 3: { /* green channel */ if(colorcor) value[1]=linearrgb_to_srgb(value[1]); value[1]=value[1]*255; /* scale to 0-255 range */ ivalue=(int)value[1]; break; } case 4: /*blue channel */ { if(colorcor) value[2]=linearrgb_to_srgb(value[2]); value[2]=value[2]*255; /* scale to 0-255 range */ ivalue=(int)value[2]; break; } case 5: /* luminence */ { if(colorcor) linearrgb_to_srgb_v3_v3(&value[0],&value[0]); rgb_to_yuv(value[0],value[1],value[2], &value[0], &value[1], &value[2]); value[0]=value[0]*255; /* scale to 0-255 range */ ivalue=(int)value[0]; break; } } /*end switch */ /*clip*/ if(ivalue<0) ivalue=0; if(ivalue>255) ivalue=255; /*put in the correct bin*/ bins[ivalue]+=1; } /*end if alpha */ } } }
/* called inside thread! */ void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect) { float x1, y1, *rectf= NULL; int ymin, ymax, xmin, xmax; int rymin, rxmin, do_color_management; char *rectc; /* if renrect argument, we only refresh scanlines */ if(renrect) { /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */ if(rr->renlay==NULL || renrect->ymax>=rr->recty) return; /* xmin here is first subrect x coord, xmax defines subrect width */ xmin = renrect->xmin + rr->crop; xmax = renrect->xmax - xmin + rr->crop; if(xmax<2) return; ymin= renrect->ymin + rr->crop; ymax= renrect->ymax - ymin + rr->crop; if(ymax<2) return; renrect->ymin= renrect->ymax; } else { xmin = ymin = rr->crop; xmax = rr->rectx - 2*rr->crop; ymax = rr->recty - 2*rr->crop; } /* xmin ymin is in tile coords. transform to ibuf */ rxmin= rr->tilerect.xmin + xmin; if(rxmin >= ibuf->x) return; rymin= rr->tilerect.ymin + ymin; if(rymin >= ibuf->y) return; if(rxmin + xmax > ibuf->x) xmax= ibuf->x - rxmin; if(rymin + ymax > ibuf->y) ymax= ibuf->y - rymin; if(xmax < 1 || ymax < 1) return; /* find current float rect for display, first case is after composit... still weak */ if(rr->rectf) rectf= rr->rectf; else { if(rr->rect32) return; else { if(rr->renlay==NULL || rr->renlay->rectf==NULL) return; rectf= rr->renlay->rectf; } } if(rectf==NULL) return; if(ibuf->rect==NULL) imb_addrectImBuf(ibuf); rectf+= 4*(rr->rectx*ymin + xmin); rectc= (char *)(ibuf->rect + ibuf->x*rymin + rxmin); do_color_management = (scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)); /* XXX make nice consistent functions for this */ for(y1= 0; y1<ymax; y1++) { float *rf= rectf; float srgb[3]; char *rc= rectc; const float dither = ibuf->dither / 255.0f; /* XXX temp. because crop offset */ if(rectc >= (char *)(ibuf->rect)) { for(x1= 0; x1<xmax; x1++, rf += 4, rc+=4) { /* color management */ if(do_color_management) { srgb[0]= linearrgb_to_srgb(rf[0]); srgb[1]= linearrgb_to_srgb(rf[1]); srgb[2]= linearrgb_to_srgb(rf[2]); } else { copy_v3_v3(srgb, rf); } /* dither */ if(dither != 0.0f) { const float d = (BLI_frand()-0.5f)*dither; srgb[0] += d; srgb[1] += d; srgb[2] += d; } /* write */ rc[0]= FTOCHAR(srgb[0]); rc[1]= FTOCHAR(srgb[1]); rc[2]= FTOCHAR(srgb[2]); rc[3]= FTOCHAR(rf[3]); } } rectf += 4*rr->rectx; rectc += 4*ibuf->x; } }
/* assume converting from linear float to sRGB byte */ void IMB_rect_from_float(struct ImBuf *ibuf) { /* quick method to convert floatbuf to byte */ float *tof = (float *)ibuf->rect_float; // int do_dither = ibuf->dither != 0.f; float dither= ibuf->dither / 255.0f; float srgb[4]; int i, channels= ibuf->channels; short profile= ibuf->profile; unsigned char *to = (unsigned char *) ibuf->rect; if(tof==NULL) return; if(to==NULL) { imb_addrectImBuf(ibuf); to = (unsigned char *) ibuf->rect; } if(channels==1) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof++) to[1]= to[2]= to[3]= to[0] = FTOCHAR(tof[0]); } else if (profile == IB_PROFILE_LINEAR_RGB) { if(channels == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=3) { srgb[0]= linearrgb_to_srgb(tof[0]); srgb[1]= linearrgb_to_srgb(tof[1]); srgb[2]= linearrgb_to_srgb(tof[2]); to[0] = FTOCHAR(srgb[0]); to[1] = FTOCHAR(srgb[1]); to[2] = FTOCHAR(srgb[2]); to[3] = 255; } } else if (channels == 4) { if (dither != 0.f) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) { const float d = (BLI_frand()-0.5f)*dither; srgb[0]= d + linearrgb_to_srgb(tof[0]); srgb[1]= d + linearrgb_to_srgb(tof[1]); srgb[2]= d + linearrgb_to_srgb(tof[2]); srgb[3]= d + tof[3]; to[0] = FTOCHAR(srgb[0]); to[1] = FTOCHAR(srgb[1]); to[2] = FTOCHAR(srgb[2]); to[3] = FTOCHAR(srgb[3]); } } else { floatbuf_to_srgb_byte(tof, to, 0, ibuf->x, 0, ibuf->y, ibuf->x); } } } else if(ELEM(profile, IB_PROFILE_NONE, IB_PROFILE_SRGB)) { if(channels==3) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=3) { to[0] = FTOCHAR(tof[0]); to[1] = FTOCHAR(tof[1]); to[2] = FTOCHAR(tof[2]); to[3] = 255; } } else { if (dither != 0.f) { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) { const float d = (BLI_frand()-0.5f)*dither; float col[4]; col[0]= d + tof[0]; col[1]= d + tof[1]; col[2]= d + tof[2]; col[3]= d + tof[3]; to[0] = FTOCHAR(col[0]); to[1] = FTOCHAR(col[1]); to[2] = FTOCHAR(col[2]); to[3] = FTOCHAR(col[3]); } } else { for (i = ibuf->x * ibuf->y; i > 0; i--, to+=4, tof+=4) { to[0] = FTOCHAR(tof[0]); to[1] = FTOCHAR(tof[1]); to[2] = FTOCHAR(tof[2]); to[3] = FTOCHAR(tof[3]); } } } } /* ensure user flag is reset */ ibuf->userflags &= ~IB_RECT_INVALID; }
MINLINE void linearrgb_to_srgb_ushort4_predivide(unsigned short srgb[4], const float linear[4]) { float alpha, inv_alpha, t; int i; if (linear[3] == 1.0f || linear[3] == 0.0f) { linearrgb_to_srgb_ushort4(srgb, linear); return; } alpha = linear[3]; inv_alpha = 1.0f / alpha; for (i = 0; i < 3; ++i) { t = linear[i] * inv_alpha; srgb[i] = (t < 1.0f) ? (unsigned short) (to_srgb_table_lookup(t) * alpha) : FTOUSHORT(linearrgb_to_srgb(t) * alpha); } srgb[3] = FTOUSHORT(linear[3]); }