package com.kuxhausen.huemore.persistence; import android.content.Context; import android.database.Cursor; import android.graphics.Color; import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.annotation.Size; import com.kuxhausen.huemore.persistence.Definitions.MoodColumns; import com.kuxhausen.huemore.state.Mood; import com.kuxhausen.huemore.utils.DeferredLog; import java.util.Calendar; public class Utils { public static @Nullable Mood getMoodFromDatabase(String moodName, Context ctx) { String[] moodColumns = {MoodColumns.COL_MOOD_VALUE}; String[] mWhereClause = {moodName}; Cursor moodCursor = ctx.getContentResolver().query(Definitions.MoodColumns.MOODS_URI, moodColumns, MoodColumns.COL_MOOD_NAME + "=?", mWhereClause, null); if (!moodCursor.moveToFirst()) { moodCursor.close(); return null; } String encodedMood = moodCursor.getString(0); moodCursor.close(); try { return HueUrlEncoder.decode(encodedMood).second.first; } catch (InvalidEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } catch (FutureEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } /** * Inspired by https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes * /RGB%20to%20xy%20Color%20conversion.md * * @param h in 0 to 1 in the wide RGB D65 space * @param s in 0 to 1 in the wide RGB D65 space * @return CIE 1931 xy each ranging 0 to 1 */ public static float[] hsTOxy(float[] input) { float h = input[0]; float s = input[1]; h = Math.max(0f, Math.min(h, 1f)); s = Math.max(0f, Math.min(s, 1f)); float[] hsv = {h * 360, s, 1}; int rgb = Color.HSVToColor(hsv); float red = ((rgb >>> 16) & 0xFF) / 255f; float green = ((rgb >>> 8) & 0xFF) / 255f; float blue = ((rgb) & 0xFF) / 255f; red = (float) ((red > 0.04045f) ? Math.pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f)); green = (float) ((green > 0.04045f) ? Math.pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f)); blue = (float) ((blue > 0.04045f) ? Math.pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f)); float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f; float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f; float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f; float x = X / (X + Y + Z); float y = Y / (X + Y + Z); float[] result = {x, y}; // Log.e("colorspace", "h"+h+" s"+s+" to x"+x+" y"+y); return result; } /** * Inspired by https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes * /RGB%20to%20xy%20Color%20conversion.md * * @param x CIE 1931 x ranging from 0 to 1 * @param y CIE 1931 y ranging from 0 to 1 * @return h, s each ranging 0 to 1 in the wide RGB D65 space */ public static float[] xyTOhs(float[] input) { float x = input[0]; float y = input[1]; float z = 1.0f - x - y; float Y = 1f; // The given brightness value float X = (Y / y) * x; float Z = (Y / y) * z; float r = X * 1.612f - Y * 0.203f - Z * 0.302f; float g = -X * 0.509f + Y * 1.412f + Z * 0.066f; float b = X * 0.026f - Y * 0.072f + Z * 0.962f; r = (float) (r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * Math.pow(r, (1.0f / 2.4f)) - 0.055f); g = (float) (g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * Math.pow(g, (1.0f / 2.4f)) - 0.055f); b = (float) (b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * Math.pow(b, (1.0f / 2.4f)) - 0.055f); float max = Math.max(r, Math.max(g, b)); r = r / max; g = g / max; b = b / max; r = Math.max(r, 0); g = Math.max(g, 0); b = Math.max(b, 0); float[] hsv = new float[3]; Color.RGBToHSV((int) (r * 0xFF), (int) (g * 0xFF), (int) (b * 0xFF), hsv); float h = hsv[0] / 360; float s = hsv[1]; h = Math.max(0f, Math.min(h, 1f)); s = Math.max(0f, Math.min(s, 1f)); float[] result = {h, s}; // Log.e("colorspace", "h"+h+" s"+s+" from x"+x+" y"+y); return result; } /** * @param x CIE 1931 x ranging from 0 to 1 * @param y CIE 1931 y ranging from 0 to 1 * @return h, s each ranging 0 to 1 in the sRGB D65 space */ public static float[] xyTOsRGBhs(float[] input) { float x = input[0]; float y = input[1]; float z = 1.0f - x - y; float Y = 1f; // The given brightness value float X = (Y / y) * x; float Z = (Y / y) * z; float r = X * 3.2404542f + Y * -1.5371385f + Z * -0.4985314f; float g = X * -0.9692660f + Y * 1.8760108f + Z * 0.0415560f; float b = X * 0.0556434f + Y * -0.2040259f + Z * 1.0572252f; r = (float) (r <= 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * Math.pow(r, (1.0f / 2.4f)) - 0.055f); g = (float) (g <= 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * Math.pow(g, (1.0f / 2.4f)) - 0.055f); b = (float) (b <= 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * Math.pow(b, (1.0f / 2.4f)) - 0.055f); float max = Math.max(r, Math.max(g, b)); r = r / max; g = g / max; b = b / max; r = Math.max(r, 0); g = Math.max(g, 0); b = Math.max(b, 0); float[] hsv = new float[3]; Color.RGBToHSV((int) (r * 0xFF), (int) (g * 0xFF), (int) (b * 0xFF), hsv); float h = hsv[0] / 360; float s = hsv[1]; h = Math.max(0f, Math.min(h, 1f)); s = Math.max(0f, Math.min(s, 1f)); float[] result = {h, s}; // Log.e("colorspace", "h"+h+" s"+s+" from x"+x+" y"+y); return result; } /** * Plankian locus cubic spline approximation by Kim et al inspired by * http://en.wikipedia.org/wiki/Planckian_locus * * @param ct Planckian locus color temperature in Mirads * @return x, y in CIE 1931 each ranging 0 to 1 */ public static float[] ctTOxy(float ctMirads) { double ct = 1000000 / ctMirads; double xc = 0; double yc = 0; if (1667 <= ct && ct <= 4000) { xc = -0.2661239 * (Math.pow(10, 9) / Math.pow(ct, 3)) - 0.2343580 * (Math.pow(10, 6) / Math.pow(ct, 2)) + 0.8776956 * (Math.pow(10, 3) / ct) + 0.179910d; } else if (4000 < ct && ct < 25000) { xc = -3.0258469 * (Math.pow(10, 9) / Math.pow(ct, 3)) + 2.1070379 * (Math.pow(10, 6) / Math.pow(ct, 2)) + 0.2226347 * (Math.pow(10, 3) / ct) + 0.240390d; } if (1667 <= ct && ct <= 2222) { yc = -1.1063814 * Math.pow(xc, 3) - 1.34811020 * Math.pow(xc, 2) + 2.18555832 * xc - 0.20219683d; } else if (2222 < ct && ct <= 4000) { yc = -0.9549476 * Math.pow(xc, 3) - 1.37418593 * Math.pow(xc, 2) + 2.09137015 * xc - 0.16748867d; } else if (4000 < ct && ct <= 25000) { yc = 3.0817580 * Math.pow(xc, 3) - 5.87338670 * Math.pow(xc, 2) + 3.75112997 * xc - 0.37001483d; } DeferredLog.e("ctConversion", "%f , %f , %f", ct, + xc, yc); float[] result = {(float) xc, (float) yc}; return result; } /** * @param gamutBounds list of xy coordinates of maximum reachable colors * @param xy desired coordinates * @return the desired coordinates, adjusted to be within bounds */ public static float[] toReachableXY(@Size(6) float[] gamutBounds, @Size(2) float[] xy) { //TODO return xy; } /** * @return deciseconds */ public static int toDeciSeconds(long milliseconds) { return (int) (milliseconds / 100l); } /** * @return milliseconds */ public static long fromDeciSeconds(int deciseconds) { return deciseconds * 100l; } public static int moodDailyTimeFromCalendarMillis(Calendar input) { Calendar startOfDay = Calendar.getInstance(); startOfDay.set(Calendar.MILLISECOND, 0); startOfDay.set(Calendar.SECOND, 0); startOfDay.set(Calendar.MINUTE, 0); startOfDay.set(Calendar.HOUR_OF_DAY, 0); Calendar inputCopy = Calendar.getInstance(); inputCopy.setTimeInMillis(input.getTimeInMillis()); Long offsetWithinTheDayInMilis = inputCopy.getTimeInMillis() - startOfDay.getTimeInMillis(); return (int) (offsetWithinTheDayInMilis / 100); } public static Calendar calendarMillisFromMoodDailyTime(int dailyMoodDeciSeconds) { Calendar startOfDay = Calendar.getInstance(); startOfDay.set(Calendar.MILLISECOND, 0); startOfDay.set(Calendar.SECOND, 0); startOfDay.set(Calendar.MINUTE, 0); startOfDay.set(Calendar.HOUR_OF_DAY, 0); startOfDay.getTime(); startOfDay.setTimeInMillis(startOfDay.getTimeInMillis() + (dailyMoodDeciSeconds * 100L)); return startOfDay; } /** * @return the start of day measured in SystemClock.elapsedRealtime milliseconds (may be negative) */ public static long getDayStartElapsedRealTimeMillis() { Calendar dayStart = Calendar.getInstance(); dayStart.set(Calendar.MILLISECOND, 0); dayStart.set(Calendar.SECOND, 0); dayStart.set(Calendar.MINUTE, 0); dayStart.set(Calendar.HOUR_OF_DAY, 0); dayStart.getTime(); Calendar current = Calendar.getInstance(); return SystemClock.elapsedRealtime() - (current.getTimeInMillis() - dayStart.getTimeInMillis()); } }