package net.tropicraft.drinks; public final class ColorMixer { private static ColorMixer instance = new ColorMixer(); public ColorMixer() { } public static ColorMixer getInstance() { return instance; } public void normalizeRGBA(int[] rgba, float[] result) { result[0] = rgba[0]/255f; result[1] = rgba[1]/255f; result[2] = rgba[2]/255f; result[3] = rgba[3]/255f; } public void denormalizeRGBA(float[] rgba, int[] result) { result[0] = (int)(255*rgba[0]); result[1] = (int)(255*rgba[1]); result[2] = (int)(255*rgba[2]); result[3] = (int)(255*rgba[3]); } public void splitRGBA(long color, int[] result) { result[0] = (int)((color>>24) & 0xff); result[1] = (int)((color>>16) & 0xff); result[2] = (int)((color>>8) & 0xff); result[3] = (int)(color & 0xff); } public long unsplitRGBA(int[] rgb) { return ((rgb[0]&0xff)<<24L)|((rgb[1]&0xff)<<16L)|((rgb[2]&0xff)<<8L)|(rgb[3]&0xff); } public void normalizeRGB(int[] rgb, float[] result) { result[0] = rgb[0]/255f; result[1] = rgb[1]/255f; result[2] = rgb[2]/255f; } public void denormalizeRGB(float[] rgb, int[] result) { result[0] = (int)(255*rgb[0]); result[1] = (int)(255*rgb[1]); result[2] = (int)(255*rgb[2]); } public void splitRGB(int color, int[] result) { result[0] = (color>>16) & 0xff; result[1] = (color>>8) & 0xff; result[2] = color & 0xff; } public int unsplitRGB(int[] rgb) { return ((rgb[0]&0xff)<<16)|((rgb[1]&0xff)<<8)|(rgb[2]&0xff); } public void convertRGBToCMYK(float[] rgb, float[] cmyk) { float tempC = 1 - rgb[0]; float tempM = 1 - rgb[1]; float tempY = 1 - rgb[2]; float black = Math.min(tempC,Math.min(tempM,tempY)); float cyan = (tempC - black) / (1-black); float magenta = (tempM - black) / (1-black); float yellow = (tempY - black) / (1-black); cmyk[0] = cyan; cmyk[1] = magenta; cmyk[2] = yellow; cmyk[3] = black; } public void convertCMYKToRGB(float[] cmyk, float[] rgb) { float c = cmyk[0]; float m = cmyk[1]; float y = cmyk[2]; float k = cmyk[3]; float nc = c * (1-k) + k; float nm = m * (1-k) + k; float ny = y * (1-k) + k; float r = 1-nc; float g = 1-nm; float b = 1-ny; rgb[0] = r; rgb[1] = g; rgb[2] = b; } public void convertRYBToRGB(float[] ryb, float[] rgb) { float r = ryb[0]; float y = ryb[1]; float b = ryb[2]; // remove whiteness float w = Math.min(r, Math.min(y, b)); r -= w; y -= w; b -= w; float my = Math.max(r, Math.max(y, b)); // get green out of yellow and blue float g = Math.min(y, b); y -= g; b -= g; if (b != 0 && g != 0) { b *= 2; g *= 2; } // redistribute remaining yellow r += y; g += y; // normalize to values float mg = Math.max(r, Math.max(g, b)); if (mg != 0) { float n = my / mg; r *= n; g *= n; b *= n; } // add the white back in r += w; g += w; b += w; rgb[0] = r; rgb[1] = g; rgb[2] = b; } public void convertRGBToRYB(float[] rgb, float[] ryb) { float r = rgb[0]; float g = rgb[1]; float b = rgb[2]; // remove white float w = Math.min(r,Math.min(g, b)); r -= w; g -= w; b -= w; float mg = Math.max(r, Math.max(g, b)); // remove yellow float y = Math.min(r, g); r -= y; g -= y; // if both blue and green then cut in half to preserve range if (b != 0 && g != 0) { b /= 2f; g /= 2f; } // redistribute the green y += g; b += g; float my = Math.max(r, Math.max(y, b)); // normalize to values if (my != 0) { float n = mg/my; r *= n; y *= n; b *= n; } // add back in white r += w; y += w; b += w; ryb[0] = r; ryb[1] = y; ryb[2] = b; } public void mixCMYK(float[][] cmyks, float[] result) { if (cmyks.length == 0) { result[0] = result[1] = result[2] = result[3] = 0; return; } if (cmyks.length == 1) { float[] cmyk = cmyks[0]; result[0] = cmyk[0]; result[1] = cmyk[1]; result[2] = cmyk[2]; result[3] = cmyk[3]; return; } float cTotal = 0f; float mTotal = 0f; float yTotal = 0f; float kTotal = 0f; float cMax = 0f; float mMax = 0f; float yMax = 0f; float kMax = 0f; for (float[] cmyk: cmyks) { float c = cmyk[0]; float m = cmyk[1]; float y = cmyk[2]; float k = cmyk[3]; cTotal += c; mTotal += m; yTotal += y; kTotal += k; cMax = c > cMax ? c : cMax; mMax = m > mMax ? m : mMax; yMax = y > yMax ? y : yMax; kMax = k > kMax ? k : kMax; } int count = cmyks.length; float c = cTotal/(float)Math.sqrt(count+1); float m = mTotal/(float)Math.sqrt(count+1); float y = yTotal/(float)Math.sqrt(count+1); float k = kTotal/(float)Math.sqrt(Math.sqrt(count)); result[0] = c; result[1] = m; result[2] = y; result[3] = k; } public void mixRYB(float[][] rybs, float[] result) { if (rybs.length == 0) { result[0] = result[1] = result[2] = 0; return; } if (rybs.length == 1) { float[] ryb = rybs[0]; result[0] = ryb[0]; result[1] = ryb[1]; result[2] = ryb[2]; return; } float rTotal = 0; float yTotal = 0; float bTotal = 0; for (float[] ryb: rybs) { rTotal += ryb[0]; yTotal += ryb[1]; bTotal += ryb[2]; } int count = rybs.length; float br = rTotal/count; float r = rTotal/(float)Math.sqrt(Math.sqrt(count-br)); br = yTotal/count; float y = yTotal/(float)Math.sqrt(Math.sqrt(count-br)); br = bTotal/count; float b = bTotal/(float)Math.sqrt(Math.sqrt(count-br)); result[0] = r; result[1] = y; result[2] = b; } public int mixRGBAsCMYK(int[] rgbs) { float[][] cmyks = new float[rgbs.length][]; int[] tempRGBi = new int[3]; float[] tempRGBf = new float[3]; float[] tempCMYKf = new float[4]; for (int i = 0; i < rgbs.length; ++i) { int rgb = rgbs[i]; splitRGB(rgb, tempRGBi); normalizeRGB(tempRGBi, tempRGBf); convertRGBToCMYK(tempRGBf, tempCMYKf); cmyks[i] = tempCMYKf; } mixCMYK(cmyks, tempCMYKf); convertCMYKToRGB(tempCMYKf, tempRGBf); denormalizeRGB(tempRGBf, tempRGBi); int rgb = unsplitRGB(tempRGBi); return rgb; } public int mixRGBAsRYB(int[] rgbs) { float[][] rybs = new float[rgbs.length][]; int[] tempRGBi = new int[3]; float[] tempRGBf = new float[3]; float[] tempRYBf = new float[3]; for (int i = 0; i < rgbs.length; ++i) { int rgb = rgbs[i]; splitRGB(rgb, tempRGBi); normalizeRGB(tempRGBi, tempRGBf); convertRGBToRYB(tempRGBf, tempRYBf); rybs[i] = tempRYBf; } mixRYB(rybs, tempRYBf); convertRYBToRGB(tempRYBf, tempRGBf); denormalizeRGB(tempRGBf, tempRGBi); int rgb = unsplitRGB(tempRGBi); return rgb; } public int alphaBlendRGBA(int bg, int fg, float fgAlpha) { float bgRed = ((bg>>16) & 0xff) / 255f; float bgGreen = ((bg>>8) & 0xff) / 255f; float bgBlue = (bg & 0xff) / 255f; float fgRed = ((fg>>16) & 0xff) / 255f; float fgGreen = ((fg>>8) & 0xff) / 255f; float fgBlue = (fg & 0xff) / 255f; float outRed, outGreen, outBlue; outRed = fgRed*fgAlpha + bgRed*(1f-fgAlpha); outGreen = fgGreen*fgAlpha + bgGreen*(1f-fgAlpha); outBlue = fgBlue*fgAlpha + bgBlue*(1f-fgAlpha); int outRedi = (int)(outRed*255f); int outGreeni = (int)(outGreen*255f); int outBluei = (int)(outBlue*255f); return ((outRedi&0xff)<<16)|((outGreeni&0xff)<<8)|(outBluei&0xff); } // public static void main(String[] args) { // int bgColor = Integer.parseInt(args[0], 16); // int fgColor = Integer.parseInt(args[1], 16); // float fgAlpha = Float.parseFloat(args[2]); // // int result = ColorMixer.getInstance().alphaBlendRGBA(bgColor, fgColor, fgAlpha); // System.out.println(String.format("%06x + %06x@%.1f = %06x", bgColor, fgColor, fgAlpha, result)); // } }