static void rgbtobw_valuefn( float *out, TexParams *p, bNode *UNUSED(node), bNodeStack **in, short thread) { float cin[4]; tex_input_rgba(cin, in[0], p, thread); *out = IMB_colormanagement_get_luminance(cin); }
static void node_shader_exec_rgbtobw(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **out) { /* stack order out: bw */ /* stack order in: col */ float col[3]; nodestack_get_vec(col, SOCK_VECTOR, in[0]); out[0]->vec[0] = IMB_colormanagement_get_luminance(col); }
/* no profile conversion */ void IMB_color_to_bw(ImBuf *ibuf) { float *rct_fl = ibuf->rect_float; uchar *rct = (uchar *)ibuf->rect; size_t i; if (rct_fl) { for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct_fl += 4) rct_fl[0] = rct_fl[1] = rct_fl[2] = IMB_colormanagement_get_luminance(rct_fl); } if (rct) { for (i = ((size_t)ibuf->x * ibuf->y); i > 0; i--, rct += 4) rct[0] = rct[1] = rct[2] = IMB_colormanagement_get_luminance_byte(rct); } }
/* Traces a shadow through the object, * pretty much gets the transmission over a ray path */ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is) { float hitco[3]; float tr[3] = {1.0, 1.0, 1.0}; Isect is = {{0}}; const float *startco, *endco; memset(shr, 0, sizeof(ShadeResult)); /* if 1st hit normal is facing away from the camera, * then we're inside the volume already. */ if (shi->flippednor) { startco = last_is->start; endco = shi->co; } /* trace to find a backface, the other side bounds of the volume */ /* (ray intersect ignores front faces here) */ else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) { startco = shi->co; endco = hitco; } else { shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f; shr->alpha = shr->combined[3] = 1.f; return; } vol_get_transmittance(shi, tr, startco, endco); /* if we hit another face in the same volume bounds */ /* shift raytrace coordinates to the hit point, to avoid shading volume twice */ /* due to idiosyncracy in ray_trace_shadow_tra() */ if (is.hit.ob == shi->obi) { copy_v3_v3(shi->co, hitco); last_is->dist += is.dist; shi->vlr = (VlakRen *)is.hit.face; } copy_v3_v3(shr->combined, tr); shr->combined[3] = 1.0f - IMB_colormanagement_get_luminance(tr); shr->alpha = shr->combined[3]; }
static ImBuf *make_waveform_view_from_ibuf_float(ImBuf *ibuf) { ImBuf *rval = IMB_allocImBuf(ibuf->x + 3, 515, 32, IB_rect); int x, y; const float *src = ibuf->rect_float; unsigned char *tgt = (unsigned char *) rval->rect; int w = ibuf->x + 3; int h = 515; float waveform_gamma = 0.2; unsigned char wtable[256]; wform_put_grid(tgt, w, h); for (x = 0; x < 256; x++) { wtable[x] = (unsigned char) (pow(((float) x + 1) / 256, waveform_gamma) * 255); } for (y = 0; y < ibuf->y; y++) { unsigned char *last_p = NULL; for (x = 0; x < ibuf->x; x++) { const float *rgb = src + 4 * (ibuf->x * y + x); float v = IMB_colormanagement_get_luminance(rgb); unsigned char *p = tgt; CLAMP(v, 0.0f, 1.0f); p += 4 * (w * ((int) (v * (h - 3)) + 1) + x + 1); scope_put_pixel(wtable, p); p += 4 * w; scope_put_pixel(wtable, p); if (last_p != NULL) { wform_put_line(w, last_p, p); } last_p = p; } } wform_put_border(tgt, w, h); return rval; }
void LuminanceMatteOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float inColor[4]; this->m_inputImageProgram->readSampled(inColor, x, y, sampler); const float high = this->m_settings->t1; const float low = this->m_settings->t2; const float luminance = IMB_colormanagement_get_luminance(inColor); float alpha; /* one line thread-friend algorithm: * output[0] = min(inputValue[3], min(1.0f, max(0.0f, ((luminance - low) / (high - low)))); */ /* test range */ if (luminance > high) { alpha = 1.0f; } else if (luminance < low) { alpha = 0.0f; } else { /*blend */ alpha = (luminance - low) / (high - low); } /* store matte(alpha) value in [0] to go with * COM_SetAlphaOperation and the Value output */ /* don't make something that was more transparent less transparent */ output[0] = min_ff(alpha, inColor[3]); }
void ColorCorrectionOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float inputImageColor[4]; float inputMask[4]; this->m_inputImage->readSampled(inputImageColor, x, y, sampler); this->m_inputMask->readSampled(inputMask, x, y, sampler); float level = (inputImageColor[0] + inputImageColor[1] + inputImageColor[2]) / 3.0f; float contrast = this->m_data->master.contrast; float saturation = this->m_data->master.saturation; float gamma = this->m_data->master.gamma; float gain = this->m_data->master.gain; float lift = this->m_data->master.lift; float r, g, b; float value = inputMask[0]; value = min(1.0f, value); const float mvalue = 1.0f - value; float levelShadows = 0.0; float levelMidtones = 0.0; float levelHighlights = 0.0; #define MARGIN 0.10f #define MARGIN_DIV (0.5f / MARGIN) if (level < this->m_data->startmidtones - MARGIN) { levelShadows = 1.0f; } else if (level < this->m_data->startmidtones + MARGIN) { levelMidtones = ((level - this->m_data->startmidtones) * MARGIN_DIV) + 0.5f; levelShadows = 1.0f - levelMidtones; } else if (level < this->m_data->endmidtones - MARGIN) { levelMidtones = 1.0f; } else if (level < this->m_data->endmidtones + MARGIN) { levelHighlights = ((level - this->m_data->endmidtones) * MARGIN_DIV) + 0.5f; levelMidtones = 1.0f - levelHighlights; } else { levelHighlights = 1.0f; } #undef MARGIN #undef MARGIN_DIV contrast *= (levelShadows * this->m_data->shadows.contrast) + (levelMidtones * this->m_data->midtones.contrast) + (levelHighlights * this->m_data->highlights.contrast); saturation *= (levelShadows * this->m_data->shadows.saturation) + (levelMidtones * this->m_data->midtones.saturation) + (levelHighlights * this->m_data->highlights.saturation); gamma *= (levelShadows * this->m_data->shadows.gamma) + (levelMidtones * this->m_data->midtones.gamma) + (levelHighlights * this->m_data->highlights.gamma); gain *= (levelShadows * this->m_data->shadows.gain) + (levelMidtones * this->m_data->midtones.gain) + (levelHighlights * this->m_data->highlights.gain); lift += (levelShadows * this->m_data->shadows.lift) + (levelMidtones * this->m_data->midtones.lift) + (levelHighlights * this->m_data->highlights.lift); float invgamma = 1.0f / gamma; float luma = IMB_colormanagement_get_luminance(inputImageColor); r = inputImageColor[0]; g = inputImageColor[1]; b = inputImageColor[2]; r = (luma + saturation * (r - luma)); g = (luma + saturation * (g - luma)); b = (luma + saturation * (b - luma)); r = 0.5f + ((r - 0.5f) * contrast); g = 0.5f + ((g - 0.5f) * contrast); b = 0.5f + ((b - 0.5f) * contrast); r = powf(r * gain + lift, invgamma); g = powf(g * gain + lift, invgamma); b = powf(b * gain + lift, invgamma); // mix with mask r = mvalue * inputImageColor[0] + value * r; g = mvalue * inputImageColor[1] + value * g; b = mvalue * inputImageColor[2] + value * b; if (this->m_redChannelEnabled) { output[0] = r; } else { output[0] = inputImageColor[0]; } if (this->m_greenChannelEnabled) { output[1] = g; } else { output[1] = inputImageColor[1]; } if (this->m_blueChannelEnabled) { output[2] = b; } else { output[2] = inputImageColor[2]; } output[3] = inputImageColor[3]; }
static void tonemapmodifier_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask) { SequencerTonemapModifierData *tmmd = (SequencerTonemapModifierData *) smd; AvgLogLum data; data.tmmd = tmmd; data.colorspace = (ibuf->rect_float != NULL) ? ibuf->float_colorspace : ibuf->rect_colorspace; float lsum = 0.0f; int p = ibuf->x * ibuf->y; float *fp = ibuf->rect_float; unsigned char *cp = (unsigned char *)ibuf->rect; float avl, maxl = -FLT_MAX, minl = FLT_MAX; const float sc = 1.0f / p; float Lav = 0.f; float cav[4] = {0.0f, 0.0f, 0.0f, 0.0f}; while (p--) { float pixel[4]; if (fp != NULL) { copy_v4_v4(pixel, fp); } else { straight_uchar_to_premul_float(pixel, cp); } IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, data.colorspace); float L = IMB_colormanagement_get_luminance(pixel); Lav += L; add_v3_v3(cav, pixel); lsum += logf(max_ff(L, 0.0f) + 1e-5f); maxl = (L > maxl) ? L : maxl; minl = (L < minl) ? L : minl; if (fp != NULL) { fp += 4; } else { cp += 4; } } data.lav = Lav * sc; mul_v3_v3fl(data.cav, cav, sc); maxl = logf(maxl + 1e-5f); minl = logf(minl + 1e-5f); avl = lsum * sc; data.auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f; float al = expf(avl); data.al = (al == 0.0f) ? 0.0f : (tmmd->key / al); data.igm = (tmmd->gamma == 0.0f) ? 1.0f : (1.0f / tmmd->gamma); if (tmmd->type == SEQ_TONEMAP_RD_PHOTORECEPTOR) { modifier_apply_threaded(ibuf, mask, tonemapmodifier_apply_threaded_photoreceptor, &data); } else /* if (tmmd->type == SEQ_TONEMAP_RD_SIMPLE) */ { modifier_apply_threaded(ibuf, mask, tonemapmodifier_apply_threaded_simple, &data); } }
static void tonemapmodifier_apply_threaded_photoreceptor(int width, int height, unsigned char *rect, float *rect_float, unsigned char *mask_rect, float *mask_rect_float, void *data_v) { AvgLogLum *avg = (AvgLogLum *)data_v; const float f = expf(-avg->tmmd->intensity); const float m = (avg->tmmd->contrast > 0.0f) ? avg->tmmd->contrast : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - avg->tmmd->correction, ia = 1.0f - avg->tmmd->adaptation; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int pixel_index = (y * width + x) * 4; float input[4], output[4], mask[3] = {1.0f, 1.0f, 1.0f}; /* Get input value. */ if (rect_float) { copy_v4_v4(input, &rect_float[pixel_index]); } else { straight_uchar_to_premul_float(input, &rect[pixel_index]); } IMB_colormanagement_colorspace_to_scene_linear_v3(input, avg->colorspace); copy_v4_v4(output, input); /* Get mask value. */ if (mask_rect_float) { copy_v3_v3(mask, mask_rect_float + pixel_index); } else if (mask_rect) { rgb_uchar_to_float(mask, mask_rect + pixel_index); } /* Apply correction. */ const float L = IMB_colormanagement_get_luminance(output); float I_l = output[0] + ic * (L - output[0]); float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]); float I_a = I_l + ia * (I_g - I_l); output[0] /= (output[0] + powf(f * I_a, m)); I_l = output[1] + ic * (L - output[1]); I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]); I_a = I_l + ia * (I_g - I_l); output[1] /= (output[1] + powf(f * I_a, m)); I_l = output[2] + ic * (L - output[2]); I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]); I_a = I_l + ia * (I_g - I_l); output[2] /= (output[2] + powf(f * I_a, m)); /* Apply mask. */ output[0] = input[0] * (1.0f - mask[0]) + output[0] * mask[0]; output[1] = input[1] * (1.0f - mask[1]) + output[1] * mask[1]; output[2] = input[2] * (1.0f - mask[2]) + output[2] * mask[2]; /* Copy result back. */ IMB_colormanagement_scene_linear_to_colorspace_v3(output, avg->colorspace); if (rect_float) { copy_v4_v4(&rect_float[pixel_index], output); } else { premul_float_to_straight_uchar(&rect[pixel_index], output); } } } }
static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) { bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); float threshold = s->brush->sharp_threshold; int x, y, xi, yi, xo, yo, xk, yk; float count; int out_off[2], in_off[2], dim[2]; int diff_pos[2]; float outrgb[4]; float rgba[4]; BlurKernel *kernel = s->blurkernel; dim[0] = ibufb->x; dim[1] = ibufb->y; in_off[0] = pos[0]; in_off[1] = pos[1]; out_off[0] = out_off[1] = 0; if (!tile) { IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]); if ((dim[0] == 0) || (dim[1] == 0)) return; } /* find offset inside mask buffers to sample them */ sub_v2_v2v2_int(diff_pos, out_off, in_off); for (y = 0; y < dim[1]; y++) { for (x = 0; x < dim[0]; x++) { /* get input pixel */ xi = in_off[0] + x; yi = in_off[1] + y; count = 0.0; if (tile) { paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); else zero_v4(rgba); } else { /* coordinates have been clipped properly here, it should be safe to do this */ paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); } zero_v4(outrgb); for (yk = 0; yk < kernel->side; yk++) { for (xk = 0; xk < kernel->side; xk++) { count += paint_2d_ibuf_add_if(ibuf, xi + xk - kernel->pixel_len, yi + yk - kernel->pixel_len, outrgb, tile, kernel->wdata[xk + yk * kernel->side]); } } if (count > 0.0f) { mul_v4_fl(outrgb, 1.0f / (float)count); if (sharpen) { /* subtract blurred image from normal image gives high pass filter */ sub_v3_v3v3(outrgb, rgba, outrgb); /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid * colored speckles appearing in final image, and also to check for threshold */ outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); if (fabsf(outrgb[0]) > threshold) { float mask = BKE_brush_alpha_get(s->scene, s->brush); float alpha = rgba[3]; rgba[3] = outrgb[3] = mask; /* add to enhance edges */ blend_color_add_float(outrgb, rgba, outrgb); outrgb[3] = alpha; } else copy_v4_v4(outrgb, rgba); } } else copy_v4_v4(outrgb, rgba); /* write into brush buffer */ xo = out_off[0] + x; yo = out_off[1] + y; paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); } } }
void ConvertColorToBWOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float inputColor[4]; this->m_inputOperation->readSampled(inputColor, x, y, sampler); output[0] = IMB_colormanagement_get_luminance(inputColor); }
/* For ease of use, I've also introduced a 'reflection' and 'reflection color' parameter, which isn't * physically correct. This works as an RGB tint/gain on out-scattered light, but doesn't affect the light * that is transmitted through the volume. While having wavelength dependent absorption/scattering is more correct, * it also makes it harder to control the overall look of the volume since coloring the outscattered light results * in the inverse color being transmitted through the rest of the volume. */ static void volumeintegrate(struct ShadeInput *shi, float col[4], const float co[3], const float endco[3]) { float radiance[3] = {0.f, 0.f, 0.f}; float tr[3] = {1.f, 1.f, 1.f}; float p[3] = {co[0], co[1], co[2]}; float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]}; const float stepsize = shi->mat->vol.stepsize; float t0 = 0.f; float pt0 = t0; float t1 = normalize_v3(step_vec); /* returns vector length */ t0 += stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread)); p[0] += t0 * step_vec[0]; p[1] += t0 * step_vec[1]; p[2] += t0 * step_vec[2]; mul_v3_fl(step_vec, stepsize); for (; t0 < t1; pt0 = t0, t0 += stepsize) { const float density = vol_get_density(shi, p); if (density > 0.00001f) { float scatter_col[3] = {0.f, 0.f, 0.f}, emit_col[3]; const float stepd = (t0 - pt0) * density; /* transmittance component (alpha) */ vol_get_transmittance_seg(shi, tr, stepsize, co, density); if (t0 > t1 * 0.25f) { /* only use depth cutoff after we've traced a little way into the volume */ if (IMB_colormanagement_get_luminance(tr) < shi->mat->vol.depth_cutoff) break; } vol_get_emission(shi, emit_col, p); if (shi->obi->volume_precache) { float p2[3]; p2[0] = p[0] + (step_vec[0] * 0.5f); p2[1] = p[1] + (step_vec[1] * 0.5f); p2[2] = p[2] + (step_vec[2] * 0.5f); vol_get_precached_scattering(&R, shi, scatter_col, p2); } else vol_get_scattering(shi, scatter_col, p, shi->view); radiance[0] += stepd * tr[0] * (emit_col[0] + scatter_col[0]); radiance[1] += stepd * tr[1] * (emit_col[1] + scatter_col[1]); radiance[2] += stepd * tr[2] * (emit_col[2] + scatter_col[2]); } add_v3_v3(p, step_vec); } /* multiply original color (from behind volume) with transmittance over entire distance */ mul_v3_v3v3(col, tr, col); add_v3_v3(col, radiance); /* alpha <-- transmission luminance */ col[3] = 1.0f - IMB_colormanagement_get_luminance(tr); }
static void vol_shade_one_lamp(struct ShadeInput *shi, const float co[3], const float view[3], LampRen *lar, float lacol[3]) { float visifac, lv[3], lampdist; float tr[3] = {1.0, 1.0, 1.0}; float hitco[3], *atten_co; float p, ref_col[3]; if (lar->mode & LA_LAYER) if ((lar->lay & shi->obi->lay) == 0) return; if ((lar->lay & shi->lay) == 0) return; if (lar->energy == 0.0f) return; if ((visifac = lamp_get_visibility(lar, co, lv, &lampdist)) == 0.f) return; copy_v3_v3(lacol, &lar->r); if (lar->mode & LA_TEXTURE) { shi->osatex = 0; do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE); } mul_v3_fl(lacol, visifac); if (ELEM(lar->type, LA_SUN, LA_HEMI)) copy_v3_v3(lv, lar->vec); negate_v3(lv); if (shi->mat->vol.shade_type == MA_VOL_SHADE_SHADOWED) { mul_v3_fl(lacol, vol_get_shadow(shi, lar, co)); } else if (ELEM(shi->mat->vol.shade_type, MA_VOL_SHADE_SHADED, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) { Isect is; if (shi->mat->vol.shadeflag & MA_VOL_RECV_EXT_SHADOW) { mul_v3_fl(lacol, vol_get_shadow(shi, lar, co)); if (IMB_colormanagement_get_luminance(lacol) < 0.001f) return; } /* find minimum of volume bounds, or lamp coord */ if (vol_get_bounds(shi, co, lv, hitco, &is, VOL_BOUNDS_SS)) { float dist = len_v3v3(co, hitco); VlakRen *vlr = (VlakRen *)is.hit.face; /* simple internal shadowing */ if (vlr->mat->material_type == MA_TYPE_SURFACE) { lacol[0] = lacol[1] = lacol[2] = 0.0f; return; } if (ELEM(lar->type, LA_SUN, LA_HEMI)) /* infinite lights, can never be inside volume */ atten_co = hitco; else if (lampdist < dist) { atten_co = lar->co; } else atten_co = hitco; vol_get_transmittance(shi, tr, co, atten_co); mul_v3_v3v3(lacol, lacol, tr); } else { /* Point is on the outside edge of the volume, * therefore no attenuation, full transmission. * Radiance from lamp remains unchanged */ } } if (IMB_colormanagement_get_luminance(lacol) < 0.001f) return; normalize_v3(lv); p = vol_get_phasefunc(shi, shi->mat->vol.asymmetry, view, lv); /* physically based scattering with non-physically based RGB gain */ vol_get_reflection_color(shi, ref_col, co); lacol[0] *= p * ref_col[0]; lacol[1] *= p * ref_col[1]; lacol[2] *= p * ref_col[2]; }
static void scopes_update_cb(void *userdata, void *userdata_chunk, const int y, const int UNUSED(threadid)) { const ScopesUpdateData *data = userdata; Scopes *scopes = data->scopes; const ImBuf *ibuf = data->ibuf; struct ColormanageProcessor *cm_processor = data->cm_processor; const unsigned char *display_buffer = data->display_buffer; const int ycc_mode = data->ycc_mode; ScopesUpdateDataChunk *data_chunk = userdata_chunk; unsigned int *bin_lum = data_chunk->bin_lum; unsigned int *bin_r = data_chunk->bin_r; unsigned int *bin_g = data_chunk->bin_g; unsigned int *bin_b = data_chunk->bin_b; unsigned int *bin_a = data_chunk->bin_a; float *min = data_chunk->min; float *max = data_chunk->max; const float *rf = NULL; const unsigned char *rc = NULL; const int rows_per_sample_line = ibuf->y / scopes->sample_lines; const int savedlines = y / rows_per_sample_line; const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0; const bool is_float = (ibuf->rect_float != NULL); if (is_float) rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels; else { rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels; } for (int x = 0; x < ibuf->x; x++) { float rgba[4], ycc[3], luma; if (is_float) { switch (ibuf->channels) { case 4: copy_v4_v4(rgba, rf); IMB_colormanagement_processor_apply_v4(cm_processor, rgba); break; case 3: copy_v3_v3(rgba, rf); IMB_colormanagement_processor_apply_v3(cm_processor, rgba); rgba[3] = 1.0f; break; case 2: copy_v3_fl(rgba, rf[0]); rgba[3] = rf[1]; break; case 1: copy_v3_fl(rgba, rf[0]); rgba[3] = 1.0f; break; default: BLI_assert(0); } } else { for (int c = 4; c--;) rgba[c] = rc[c] * INV_255; } /* we still need luma for histogram */ luma = IMB_colormanagement_get_luminance(rgba); /* check for min max */ if (ycc_mode == -1) { minmax_v3v3_v3(min, max, rgba); } else { rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode); mul_v3_fl(ycc, INV_255); minmax_v3v3_v3(min, max, ycc); } /* increment count for histo*/ bin_lum[get_bin_float(luma)]++; bin_r[get_bin_float(rgba[0])]++; bin_g[get_bin_float(rgba[1])]++; bin_b[get_bin_float(rgba[2])]++; bin_a[get_bin_float(rgba[3])]++; /* save sample if needed */ if (do_sample_line) { const float fx = (float)x / (float)ibuf->x; const int idx = 2 * (ibuf->x * savedlines + x); save_sample_line(scopes, idx, fx, rgba, ycc); } rf += ibuf->channels; rc += ibuf->channels; } }
void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) { int i, x, y; const float *fp; unsigned char *cp; int x1 = 0.5f + hist->co[0][0] * ibuf->x; int x2 = 0.5f + hist->co[1][0] * ibuf->x; int y1 = 0.5f + hist->co[0][1] * ibuf->y; int y2 = 0.5f + hist->co[1][1] * ibuf->y; struct ColormanageProcessor *cm_processor = NULL; hist->channels = 3; hist->x_resolution = 256; hist->xmax = 1.0f; /* hist->ymax = 1.0f; */ /* now do this on the operator _only_ */ if (ibuf->rect == NULL && ibuf->rect_float == NULL) return; if (ibuf->rect_float) cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings); for (i = 0; i < 256; i++) { x = (int)(0.5f + x1 + (float)i * (x2 - x1) / 255.0f); y = (int)(0.5f + y1 + (float)i * (y2 - y1) / 255.0f); if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y) { hist->data_luma[i] = hist->data_r[i] = hist->data_g[i] = hist->data_b[i] = hist->data_a[i] = 0.0f; } else { if (ibuf->rect_float) { float rgba[4]; fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); switch (ibuf->channels) { case 4: copy_v4_v4(rgba, fp); IMB_colormanagement_processor_apply_v4(cm_processor, rgba); break; case 3: copy_v3_v3(rgba, fp); IMB_colormanagement_processor_apply_v3(cm_processor, rgba); rgba[3] = 1.0f; break; case 2: copy_v3_fl(rgba, fp[0]); rgba[3] = fp[1]; break; case 1: copy_v3_fl(rgba, fp[0]); rgba[3] = 1.0f; break; default: BLI_assert(0); } hist->data_luma[i] = IMB_colormanagement_get_luminance(rgba); hist->data_r[i] = rgba[0]; hist->data_g[i] = rgba[1]; hist->data_b[i] = rgba[2]; hist->data_a[i] = rgba[3]; } else if (ibuf->rect) { cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x); hist->data_luma[i] = (float)IMB_colormanagement_get_luminance_byte(cp) / 255.0f; hist->data_r[i] = (float)cp[0] / 255.0f; hist->data_g[i] = (float)cp[1] / 255.0f; hist->data_b[i] = (float)cp[2] / 255.0f; hist->data_a[i] = (float)cp[3] / 255.0f; } } } if (cm_processor) IMB_colormanagement_processor_free(cm_processor); }
int imb_savepng(struct ImBuf *ibuf, const char *name, int flags) { png_structp png_ptr; png_infop info_ptr; unsigned char *pixels = NULL; unsigned char *from, *to; unsigned short *pixels16 = NULL, *to16; float *from_float, from_straight[4]; png_bytepp row_pointers = NULL; int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY; FILE *fp = NULL; bool is_16bit = (ibuf->foptions.flag & PNG_16BIT) != 0; bool has_float = (ibuf->rect_float != NULL); int channels_in_float = ibuf->channels ? ibuf->channels : 4; float (*chanel_colormanage_cb)(float); size_t num_bytes; /* use the jpeg quality setting for compression */ int compression; compression = (int)(((float)(ibuf->foptions.quality) / 11.1111f)); compression = compression < 0 ? 0 : (compression > 9 ? 9 : compression); if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) { /* float buffer was managed already, no need in color space conversion */ chanel_colormanage_cb = channel_colormanage_noop; } else { /* standard linear-to-srgb conversion if float buffer wasn't managed */ chanel_colormanage_cb = linearrgb_to_srgb; } /* for prints */ if (flags & IB_mem) name = "<memory>"; bytesperpixel = (ibuf->planes + 7) >> 3; if ((bytesperpixel > 4) || (bytesperpixel == 2)) { printf("imb_savepng: Unsupported bytes per pixel: %d for file: '%s'\n", bytesperpixel, name); return (0); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { printf("imb_savepng: Cannot png_create_write_struct for file: '%s'\n", name); return 0; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); printf("imb_savepng: Cannot png_create_info_struct for file: '%s'\n", name); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); printf("imb_savepng: Cannot setjmp for file: '%s'\n", name); return 0; } /* copy image data */ num_bytes = ((size_t)ibuf->x) * ibuf->y * bytesperpixel; if (is_16bit) pixels16 = MEM_mallocN(num_bytes * sizeof(unsigned short), "png 16bit pixels"); else pixels = MEM_mallocN(num_bytes * sizeof(unsigned char), "png 8bit pixels"); if (pixels == NULL && pixels16 == NULL) { png_destroy_write_struct(&png_ptr, &info_ptr); printf("imb_savepng: Cannot allocate pixels array of %dx%d, %d bytes per pixel for file: '%s'\n", ibuf->x, ibuf->y, bytesperpixel, name); return 0; } from = (unsigned char *) ibuf->rect; to = pixels; from_float = ibuf->rect_float; to16 = pixels16; switch (bytesperpixel) { case 4: color_type = PNG_COLOR_TYPE_RGBA; if (is_16bit) { if (has_float) { if (channels_in_float == 4) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4_v4(from_straight, from_float); to16[0] = ftoshort(chanel_colormanage_cb(from_straight[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_straight[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_straight[2])); to16[3] = ftoshort(chanel_colormanage_cb(from_straight[3])); to16 += 4; from_float += 4; } } else if (channels_in_float == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_float[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_float[2])); to16[3] = 65535; to16 += 4; from_float += 3; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[2] = to16[1] = to16[0]; to16[3] = 65535; to16 += 4; from_float++; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = UPSAMPLE_8_TO_16(from[0]); to16[1] = UPSAMPLE_8_TO_16(from[1]); to16[2] = UPSAMPLE_8_TO_16(from[2]); to16[3] = UPSAMPLE_8_TO_16(from[3]); to16 += 4; from += 4; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = from[3]; to += 4; from += 4; } } break; case 3: color_type = PNG_COLOR_TYPE_RGB; if (is_16bit) { if (has_float) { if (channels_in_float == 4) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4_v4(from_straight, from_float); to16[0] = ftoshort(chanel_colormanage_cb(from_straight[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_straight[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_straight[2])); to16 += 3; from_float += 4; } } else if (channels_in_float == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[1] = ftoshort(chanel_colormanage_cb(from_float[1])); to16[2] = ftoshort(chanel_colormanage_cb(from_float[2])); to16 += 3; from_float += 3; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16[2] = to16[1] = to16[0]; to16 += 3; from_float++; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = UPSAMPLE_8_TO_16(from[0]); to16[1] = UPSAMPLE_8_TO_16(from[1]); to16[2] = UPSAMPLE_8_TO_16(from[2]); to16 += 3; from += 4; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to += 3; from += 4; } } break; case 1: color_type = PNG_COLOR_TYPE_GRAY; if (is_16bit) { if (has_float) { float rgb[3]; if (channels_in_float == 4) { for (i = ibuf->x * ibuf->y; i > 0; i--) { premul_to_straight_v4_v4(from_straight, from_float); rgb[0] = chanel_colormanage_cb(from_straight[0]); rgb[1] = chanel_colormanage_cb(from_straight[1]); rgb[2] = chanel_colormanage_cb(from_straight[2]); to16[0] = ftoshort(IMB_colormanagement_get_luminance(rgb)); to16++; from_float += 4; } } else if (channels_in_float == 3) { for (i = ibuf->x * ibuf->y; i > 0; i--) { rgb[0] = chanel_colormanage_cb(from_float[0]); rgb[1] = chanel_colormanage_cb(from_float[1]); rgb[2] = chanel_colormanage_cb(from_float[2]); to16[0] = ftoshort(IMB_colormanagement_get_luminance(rgb)); to16++; from_float += 3; } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = ftoshort(chanel_colormanage_cb(from_float[0])); to16++; from_float++; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to16[0] = UPSAMPLE_8_TO_16(from[0]); to16++; from += 4; } } } else { for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to++; from += 4; } } break; } if (flags & IB_mem) { /* create image in memory */ imb_addencodedbufferImBuf(ibuf); ibuf->encodedsize = 0; png_set_write_fn(png_ptr, (png_voidp) ibuf, WriteData, Flush); } else { fp = BLI_fopen(name, "wb"); if (!fp) { png_destroy_write_struct(&png_ptr, &info_ptr); if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); printf("imb_savepng: Cannot open file for writing: '%s'\n", name); return 0; } png_init_io(png_ptr, fp); } #if 0 png_set_filter(png_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | PNG_FILTER_UP | PNG_FILTER_VALUE_UP | PNG_FILTER_AVG | PNG_FILTER_VALUE_AVG | PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH | PNG_ALL_FILTERS); #endif png_set_compression_level(png_ptr, compression); /* png image settings */ png_set_IHDR(png_ptr, info_ptr, ibuf->x, ibuf->y, is_16bit ? 16 : 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* image text info */ if (ibuf->metadata) { png_text *metadata; IDProperty *prop; int num_text = 0; for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) { if (prop->type == IDP_STRING) { num_text++; } } metadata = MEM_callocN(num_text * sizeof(png_text), "png_metadata"); num_text = 0; for (prop = ibuf->metadata->data.group.first; prop; prop = prop->next) { if (prop->type == IDP_STRING) { metadata[num_text].compression = PNG_TEXT_COMPRESSION_NONE; metadata[num_text].key = prop->name; metadata[num_text].text = IDP_String(prop); num_text++; } } png_set_text(png_ptr, info_ptr, metadata, num_text); MEM_freeN(metadata); } if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) { png_set_pHYs(png_ptr, info_ptr, (unsigned int)(ibuf->ppm[0] + 0.5), (unsigned int)(ibuf->ppm[1] + 0.5), PNG_RESOLUTION_METER); } /* write the file header information */ png_write_info(png_ptr, info_ptr); #ifdef __LITTLE_ENDIAN__ png_set_swap(png_ptr); #endif /* allocate memory for an array of row-pointers */ row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers"); if (row_pointers == NULL) { printf("imb_savepng: Cannot allocate row-pointers array for file '%s'\n", name); png_destroy_write_struct(&png_ptr, &info_ptr); if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); if (fp) { fclose(fp); } return 0; } /* set the individual row-pointers to point at the correct offsets */ if (is_16bit) { for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((unsigned short *)pixels16 + (((size_t)i) * ibuf->x) * bytesperpixel); } } else { for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((unsigned char *)pixels + (((size_t)i) * ibuf->x) * bytesperpixel * sizeof(unsigned char)); } } /* write out the entire image data in one call */ png_write_image(png_ptr, row_pointers); /* write the additional chunks to the PNG file (not really needed) */ png_write_end(png_ptr, info_ptr); /* clean up */ if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); MEM_freeN(row_pointers); png_destroy_write_struct(&png_ptr, &info_ptr); if (fp) { fflush(fp); fclose(fp); } return(1); }