private static final Image scaleLum(final double scale, final float[] rgb,
            final double whiteLum, int width, int height)
    {
        // pusty obraz wynikowy o odpowiednim rozmiarze
        BufferedImage retImg = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);

        // kolor bialy zeskalowany
        final double whiteSq = whiteLum * scale * whiteLum * scale;
        
        // konwersja do Yxy i skalowanie
        for (int pix = 0; pix < rgb.length; pix += 3)
        {
            double Y = RGB2XYZ[3] * rgb[pix] + RGB2XYZ[4] * rgb[pix + 1] + RGB2XYZ[5] * rgb[pix + 2];
            if (Y > 0)
            {
                double x = RGB2XYZ[0] * rgb[pix] + RGB2XYZ[1] * rgb[pix + 1] + RGB2XYZ[2] * rgb[pix + 2];
                double z = RGB2XYZ[6] * rgb[pix] + RGB2XYZ[7] * rgb[pix + 1] + RGB2XYZ[8] * rgb[pix + 2];

                final double sum = Y + x + z;
                x = (float) (x / sum);
                final double y = (float) (Y / sum);

                // skalowanie
                Y *= scale;

                // zastosowanie wspolczynnika Reinharda. Jesli wartosc
                // przeskalowana jest wieksza niz kolor bialy, jest mu
                // przypisywany bialy
                if (whiteSq > 0)
                    Y *= (1.0 + (Y / whiteSq)) / (1.0 + Y);
                else
                    Y *= 1.0 / (1.0 + Y);
                // konwersja z Yxy do CIE XYZ
                if (y > 0.0 && x > 0.0 && y > 0.0)
                {
                    x *= Y / y;
                    z = (Y / y) - x - Y;
                }
                else
                {
                    x = z = 0.0;
                }

                // konwersja z powrotem do RGB
                final float r = (float) Math.min(1.0, (XYZ2RGB[0] * x + XYZ2RGB[1] * Y + XYZ2RGB[2] * z));
                final float g = (float) Math.min(1.0, (XYZ2RGB[3] * x + XYZ2RGB[4] * Y + XYZ2RGB[5] * z));
                final float b = (float) Math.min(1.0, (XYZ2RGB[6] * x + XYZ2RGB[7] * Y + XYZ2RGB[8] * z));

                int rgbValue = 0;

                rgbValue |= Math.min(255, Math.max(0, (int) (r * 255.0))) << 16;
                rgbValue |= Math.min(255, Math.max(0, (int) (g * 255.0))) << 8;
                rgbValue |= Math.min(255, Math.max(0, (int) (b * 255.0)));

                // zapisanie do obrazu wynikowego
                retImg.setRGB((pix / 3) % width, (pix / 3) / width, rgbValue);
            }
            else
            {
                retImg.setRGB((pix / 3) % width, (pix / 3) / width, 0);
            }
        }

        return retImg;
    }