using System;
using System.Runtime.InteropServices;
/// Form2.AnalyzeColorScheme, ported to Gdk.Pixbuf.
public static class MapAudio
{
public const int MIN_INTENSITY = 50;
public const int DOMINANCE_THRESHOLD = 30;
public const int MIN_PURITY = 40;
public const int BLACK_THRESHOLD = 40;
public const int BLACK_PIXEL_CONSTANT_VOLUME = 51; // 20% of 255
public enum Region { None, Black, Red, Blue }
public class Result
{
public Region Region;
public bool Play;
public int Level; // 0..255, feeds AudioEngine.VolumeCurve
public byte R, G, B, A;
}
/// Reads one pixel out of a pixbuf. False if out of bounds.
public static bool ReadPixel(Gdk.Pixbuf pb, int x, int y,
out byte r, out byte g, out byte b, out byte a)
{
r = g = b = 0; a = 255;
if (pb == null) return false;
if (x < 0 || y < 0 || x >= pb.Width || y >= pb.Height) return false;
int n = pb.NChannels;
IntPtr p = pb.Pixels + y * pb.Rowstride + x * n;
r = Marshal.ReadByte(p, 0);
g = Marshal.ReadByte(p, 1);
b = Marshal.ReadByte(p, 2);
a = (n >= 4) ? Marshal.ReadByte(p, 3) : (byte)255;
return true;
}
public static Result Analyze(Gdk.Pixbuf map, int px, int py, bool blackAudioEnabled)
{
var res = new Result { Region = Region.None, Play = false, Level = 0 };
byte r, g, b, a;
if (!ReadPixel(map, px, py, out r, out g, out b, out a))
return res;
res.R = r; res.G = g; res.B = b; res.A = a;
int max = Math.Max(Math.Max(r, g), b);
int min = Math.Min(Math.Min(r, g), b);
int purity = max - min;
// LoadMap keys out black with AddAlpha, so background arrives as
// alpha 0, not RGB(0,0,0). This test has to come first or every
// background hit reads as a valid colour.
bool isBackground = a < 128 || max < BLACK_THRESHOLD;
if (isBackground)
{
res.Region = Region.Black;
if (blackAudioEnabled)
{
res.Play = true;
res.Level = BLACK_PIXEL_CONSTANT_VOLUME;
}
return res;
}
// RED — heart territory
if (r >= MIN_INTENSITY &&
r > g + DOMINANCE_THRESHOLD &&
r > b + DOMINANCE_THRESHOLD &&
purity >= MIN_PURITY)
{
res.Region = Region.Red;
res.Play = true;
res.Level = LevelFrom(r);
return res;
}
// BLUE — breath territory
if (b >= MIN_INTENSITY &&
b > r + DOMINANCE_THRESHOLD &&
b > g + DOMINANCE_THRESHOLD &&
purity >= MIN_PURITY)
{
res.Region = Region.Blue;
res.Play = true;
res.Level = LevelFrom(b);
return res;
}
return res; // unassigned colour -> silence
}
/// Form2: intensity 50..255 maps to volume level 80..255.
private static int LevelFrom(int intensity)
{
float t = (intensity - MIN_INTENSITY) / (255.0f - MIN_INTENSITY);
int level = (int)(80 + t * 175);
return Math.Max(80, Math.Min(255, level));
}
}