void CLASS scale_colors()
{
  unsigned bottom, right, size, row, col, ur, uc, i, x, y, c, sum[8];
  int val, dark, sat;
  double dsum[8], dmin, dmax;
  float scale_mul[4], fr, fc;
  ushort *img = 0, *pix;

#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS, 0, 2);
#endif

  if (user_mul[0])
    memcpy(pre_mul, user_mul, sizeof pre_mul);
  if (use_auto_wb || (use_camera_wb && cam_mul[0] <= 0.001f))
  {
    memset(dsum, 0, sizeof dsum);
    bottom = MIN(greybox[1] + greybox[3], height);
    right = MIN(greybox[0] + greybox[2], width);
    for (row = greybox[1]; row < bottom; row += 8)
      for (col = greybox[0]; col < right; col += 8)
      {
        memset(sum, 0, sizeof sum);
        for (y = row; y < row + 8 && y < bottom; y++)
          for (x = col; x < col + 8 && x < right; x++)
            FORC4
            {
              if (filters)
              {
                c = fcol(y, x);
                val = BAYER2(y, x);
              }
              else
                val = image[y * width + x][c];
              if (val > maximum - 25)
                goto skip_block;
              if ((val -= cblack[c]) < 0)
                val = 0;
              sum[c] += val;
              sum[c + 4]++;
              if (filters)
                break;
            }
        FORC(8) dsum[c] += sum[c];
      skip_block:;
      }
    FORC4 if (dsum[c]) pre_mul[c] = dsum[c + 4] / dsum[c];
  }
  if (use_camera_wb && cam_mul[0] > 0.001f)
  {
    memset(sum, 0, sizeof sum);
    for (row = 0; row < 8; row++)
      for (col = 0; col < 8; col++)
      {
        c = FC(row, col);
        if ((val = white[row][col] - cblack[c]) > 0)
          sum[c] += val;
        sum[c + 4]++;
      }
#ifdef LIBRAW_LIBRARY_BUILD
    if (load_raw == &LibRaw::nikon_load_sraw)
    {
      // Nikon sRAW: camera WB already applied:
      pre_mul[0] = pre_mul[1] = pre_mul[2] = pre_mul[3] = 1.0;
    }
    else
#endif
        if (sum[0] && sum[1] && sum[2] && sum[3])
      FORC4 pre_mul[c] = (float)sum[c + 4] / sum[c];
    else if (cam_mul[0] > 0.001f && cam_mul[2] > 0.001f)
      memcpy(pre_mul, cam_mul, sizeof pre_mul);
    else
    {
#ifdef LIBRAW_LIBRARY_BUILD
      imgdata.process_warnings |= LIBRAW_WARN_BAD_CAMERA_WB;
#endif
#ifdef DCRAW_VERBOSE
      fprintf(stderr, _("%s: Cannot use camera white balance.\n"), ifname);
#endif
    }
  }
#ifdef LIBRAW_LIBRARY_BUILD
  // Nikon sRAW, daylight
  if (load_raw == &LibRaw::nikon_load_sraw && !use_camera_wb
       && !use_auto_wb && cam_mul[0] > 0.001f &&
      cam_mul[1] > 0.001f && cam_mul[2] > 0.001f)
  {
    for (c = 0; c < 3; c++)
      pre_mul[c] /= cam_mul[c];
  }
#endif
  if (pre_mul[1] == 0)
    pre_mul[1] = 1;
  if (pre_mul[3] == 0)
    pre_mul[3] = colors < 4 ? pre_mul[1] : 1;
  dark = black;
  sat = maximum;
  if (threshold)
    wavelet_denoise();
  maximum -= black;
  for (dmin = DBL_MAX, dmax = c = 0; c < 4; c++)
  {
    if (dmin > pre_mul[c])
      dmin = pre_mul[c];
    if (dmax < pre_mul[c])
      dmax = pre_mul[c];
  }
  if (!highlight)
    dmax = dmin;
  FORC4 scale_mul[c] = (pre_mul[c] /= dmax) * 65535.0 / maximum;
#ifdef DCRAW_VERBOSE
  if (verbose)
  {
    fprintf(stderr, _("Scaling with darkness %d, saturation %d, and\nmultipliers"), dark, sat);
    FORC4 fprintf(stderr, " %f", pre_mul[c]);
    fputc('\n', stderr);
  }
#endif
  if (filters > 1000 && (cblack[4] + 1) / 2 == 1 && (cblack[5] + 1) / 2 == 1)
  {
    FORC4 cblack[FC(c / 2, c % 2)] += cblack[6 + c / 2 % cblack[4] * cblack[5] + c % 2 % cblack[5]];
    cblack[4] = cblack[5] = 0;
  }
  size = iheight * iwidth;
#ifdef LIBRAW_LIBRARY_BUILD
  scale_colors_loop(scale_mul);
#else
  for (i = 0; i < size * 4; i++)
  {
    if (!(val = ((ushort *)image)[i]))
      continue;
    if (cblack[4] && cblack[5])
      val -= cblack[6 + i / 4 / iwidth % cblack[4] * cblack[5] + i / 4 % iwidth % cblack[5]];
    val -= cblack[i & 3];
    val *= scale_mul[i & 3];
    ((ushort *)image)[i] = CLIP(val);
  }
#endif
  if ((aber[0] != 1 || aber[2] != 1) && colors == 3)
  {
#ifdef DCRAW_VERBOSE
    if (verbose)
      fprintf(stderr, _("Correcting chromatic aberration...\n"));
#endif
    for (c = 0; c < 4; c += 2)
    {
      if (aber[c] == 1)
        continue;
      img = (ushort *)malloc(size * sizeof *img);
      merror(img, "scale_colors()");
      for (i = 0; i < size; i++)
        img[i] = image[i][c];
      for (row = 0; row < iheight; row++)
      {
        ur = fr = (row - iheight * 0.5) * aber[c] + iheight * 0.5;
        if (ur > iheight - 2)
          continue;
        fr -= ur;
        for (col = 0; col < iwidth; col++)
        {
          uc = fc = (col - iwidth * 0.5) * aber[c] + iwidth * 0.5;
          if (uc > iwidth - 2)
            continue;
          fc -= uc;
          pix = img + ur * iwidth + uc;
          image[row * iwidth + col][c] =
              (pix[0] * (1 - fc) + pix[1] * fc) * (1 - fr) + (pix[iwidth] * (1 - fc) + pix[iwidth + 1] * fc) * fr;
        }
      }
      free(img);
    }
  }
#ifdef LIBRAW_LIBRARY_BUILD
  RUN_CALLBACK(LIBRAW_PROGRESS_SCALE_COLORS, 1, 2);
#endif
}