mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
380 lines
7.7 KiB
C++
380 lines
7.7 KiB
C++
#include "Draw.h"
|
|
|
|
namespace Upp {
|
|
|
|
Vector<ImageIml> UnpackImlDataUncompressed(const String& data)
|
|
{
|
|
Vector<ImageIml> img;
|
|
const char *s = data;
|
|
while(s + 6 * 2 + 1 <= data.End()) {
|
|
ImageIml& m = img.Add();
|
|
ImageBuffer ib(Peek16le(s + 1), Peek16le(s + 3));
|
|
m.flags = byte(*s);
|
|
ib.SetHotSpot(Point(Peek16le(s + 5), Peek16le(s + 7)));
|
|
ib.Set2ndSpot(Point(Peek16le(s + 9), Peek16le(s + 11)));
|
|
s += 13;
|
|
size_t len = ib.GetLength();
|
|
RGBA *t = ib;
|
|
const RGBA *e = t + len;
|
|
if(s + 4 * len > data.End())
|
|
break;
|
|
while(t < e) {
|
|
t->a = s[3];
|
|
t->r = s[0];
|
|
t->g = s[1];
|
|
t->b = s[2];
|
|
s += 4;
|
|
t++;
|
|
}
|
|
m.image = ib;
|
|
}
|
|
return img;
|
|
}
|
|
|
|
Vector<ImageIml> UnpackImlData(const void *ptr, int len)
|
|
{
|
|
return UnpackImlDataUncompressed(ZDecompress(ptr, len));
|
|
}
|
|
|
|
Vector<ImageIml> UnpackImlData(const String& d)
|
|
{
|
|
return UnpackImlData(~d, d.GetLength());
|
|
}
|
|
|
|
void iml_ReplaceAll(Image& tgt, const Image& src)
|
|
{ // this very special function replaces all unmodified instances of Image with new content
|
|
Image h = src; // make sure src has refcount 1, basically 'clone'
|
|
ImageBuffer ib = h;
|
|
h = ib;
|
|
if(tgt.GetSize() == h.GetSize() && tgt.data && src.data) {
|
|
tgt.data->buffer = h.data->buffer;
|
|
tgt.data->NewSerial();
|
|
}
|
|
else
|
|
tgt = src;
|
|
}
|
|
|
|
void Iml::Init(int n)
|
|
{
|
|
for(int i = 0; i < n; i++)
|
|
map.Add(name[i]);
|
|
flags.Alloc(map.GetCount(), IML_IMAGE_FLAGS_UNKNOWN);
|
|
}
|
|
|
|
void Iml::Reset()
|
|
{
|
|
for(IImage& m : map)
|
|
m.loaded = false;
|
|
}
|
|
|
|
void Iml::Skin()
|
|
{
|
|
for(int i = 0; i < map.GetCount(); i++)
|
|
if(map[i].loaded) {
|
|
map[i].loaded = false;
|
|
Get(i);
|
|
}
|
|
}
|
|
|
|
void Iml::AddData(const byte *s, int len, int count)
|
|
{
|
|
Data& d = data.Add();
|
|
d.data = (const char *)s;
|
|
d.len = len;
|
|
d.count = count;
|
|
img_count += count;
|
|
data.Shrink();
|
|
}
|
|
|
|
void Iml::AddId(int mode1, const char *name)
|
|
{
|
|
map.Add(name);
|
|
}
|
|
|
|
static StaticMutex sImlLock;
|
|
|
|
ImageIml Iml::GetRaw(int i)
|
|
{
|
|
int i0 = 0;
|
|
for(const Data& d : data) {
|
|
if(i >= i0 && i < i0 + d.count) {
|
|
ImageIml m = MakeValue(
|
|
[&] {
|
|
String key;
|
|
RawCat(key, d.data); // all is static
|
|
return key;
|
|
},
|
|
[&](Value& v) {
|
|
Vector<ImageIml>& m = CreateRawValue<Vector<ImageIml>>(v);
|
|
m = UnpackImlData(d.data, d.len);
|
|
ASSERT(m.GetCount() == d.count);
|
|
size_t sz = 0;
|
|
int ii = i0;
|
|
for(ImageIml& img : m) {
|
|
sz += img.image.GetLength();
|
|
if(premultiply)
|
|
img.image = Premultiply(img.image);
|
|
if(version == 0) // cleanup some bits that are set for legacy reasons
|
|
img.flags &= 0x3f;
|
|
flags[ii++] = img.flags;
|
|
}
|
|
return (int)sz;
|
|
}
|
|
).To<Vector<ImageIml>>()[i - i0];
|
|
m.flags |= global_flags;
|
|
return m;
|
|
}
|
|
i0 += d.count;
|
|
}
|
|
return ImageIml();
|
|
}
|
|
|
|
void Iml::Set(int i, const Image& img)
|
|
{ // TODO: MT
|
|
IImage& m = map[i];
|
|
iml_ReplaceAll(m.image, img);
|
|
m.loaded = true;
|
|
}
|
|
|
|
Image MakeImlImage(const String& id, Event<int, dword&, String&> GetRawFlags, Function<ImageIml(int)> GetRaw)
|
|
{
|
|
int scale = GetDPIScale();
|
|
bool dark = IsDarkTheme();
|
|
int best_i = -1;
|
|
int exact_scale_i = -1;
|
|
int best_dark = 10; // minimize this
|
|
int best_scale = -1; // maximize this
|
|
int ii = 0;
|
|
for(;;) {
|
|
dword flags;
|
|
String iid;
|
|
GetRawFlags(ii, flags, iid);
|
|
if(IsNull(iid))
|
|
break;
|
|
int q = iid.Find("__");
|
|
if(q > 0)
|
|
iid.Trim(q);
|
|
if(iid == id) {
|
|
bool isdark = flags & IML_IMAGE_FLAG_DARK;
|
|
int iscale = ImlFlagsToDPIScale(flags);
|
|
|
|
if(isdark == dark && iscale == scale) // found perfect match
|
|
return GetRaw(ii).image;
|
|
|
|
if(iscale == scale)
|
|
exact_scale_i = ii;
|
|
|
|
int idark = !!isdark - dark;
|
|
if(CombineCompare(best_dark, idark)(iscale, best_scale) > 0) { // prioritize color, then find highest scale
|
|
best_i = ii;
|
|
best_dark = idark;
|
|
best_scale = iscale;
|
|
}
|
|
}
|
|
ii++;
|
|
}
|
|
|
|
auto AdjustColor0 = [&](const Image& img, dword flags) { // flip light to dark if needed
|
|
if(dark && !(flags & (IML_IMAGE_FLAG_FIXED|IML_IMAGE_FLAG_FIXED_COLORS|IML_IMAGE_FLAG_DARK)))
|
|
return DarkTheme(img);
|
|
return img;
|
|
};
|
|
|
|
if(best_dark && exact_scale_i >= 0) { // dark color version not found, but we have exact scale
|
|
ImageIml m = GetRaw(exact_scale_i);
|
|
return AdjustColor0(m.image, m.flags);
|
|
}
|
|
|
|
if(best_i < 0)
|
|
return Null;
|
|
|
|
ImageIml best = GetRaw(best_i);
|
|
|
|
auto AdjustColor = [&](const Image& m) { // flip light to dark if needed
|
|
return best_dark ? AdjustColor0(m, best.flags) : m;
|
|
};
|
|
|
|
if(best.flags & (IML_IMAGE_FLAG_FIXED|IML_IMAGE_FLAG_FIXED_SIZE))
|
|
return AdjustColor(best.image);
|
|
|
|
if(best_scale == DPI_600) {
|
|
if(scale == DPI_100)
|
|
return AdjustColor(Downscale6x(best.image));
|
|
if(scale == DPI_150)
|
|
return AdjustColor(Downscale2x(DownSample2x(best.image)));
|
|
if(scale == DPI_200)
|
|
return AdjustColor(DownSample3x(best.image));
|
|
if(scale == DPI_300)
|
|
return AdjustColor(DownSample2x(best.image));
|
|
}
|
|
|
|
return DPISmartRescale(best.image, scale * best.image.GetSize() / best_scale);
|
|
}
|
|
|
|
Image MakeImlImage(const String& id, Event<int, ImageIml&, String&> GetRaw)
|
|
{
|
|
return MakeImlImage(id,
|
|
[&](int i, dword& flags, String& id) {
|
|
ImageIml m;
|
|
GetRaw(i, m, id);
|
|
flags = m.flags;
|
|
},
|
|
[&](int i) -> ImageIml {
|
|
ImageIml m;
|
|
String id;
|
|
GetRaw(i, m, id);
|
|
return m;
|
|
}
|
|
);
|
|
|
|
}
|
|
|
|
Image Iml::Get(int i)
|
|
{
|
|
IImage& m = map[i];
|
|
if(!m.loaded) {
|
|
Mutex::Lock __(sImlLock);
|
|
if(!m.loaded) {
|
|
Image h = MakeImlImage(GetId(i),
|
|
[&](int i, dword& rflags, String& id) {
|
|
id.Clear();
|
|
rflags = 0;
|
|
if(i < img_count) {
|
|
id = map.GetKey(i);
|
|
if(flags[i] != IML_IMAGE_FLAGS_UNKNOWN)
|
|
rflags = flags[i];
|
|
else
|
|
rflags = GetRaw(i).flags;
|
|
}
|
|
},
|
|
[&](int i) -> ImageIml {
|
|
return GetRaw(i);
|
|
}
|
|
);
|
|
iml_ReplaceAll(m.image, h);
|
|
m.loaded = true;
|
|
}
|
|
}
|
|
return m.image;
|
|
}
|
|
|
|
int ImlFlagsToDPIScale(int imlflags) {
|
|
int scale = DPI_100;
|
|
if(imlflags & IML_IMAGE_FLAG_UHD)
|
|
scale = DPI_200;
|
|
if(imlflags & IML_IMAGE_FLAG_S3)
|
|
scale *= 3;
|
|
if(imlflags & IML_IMAGE_FLAG_QHD)
|
|
scale++;
|
|
return scale;
|
|
}
|
|
|
|
int DPIScaleToImlFlags(int dpiscale) {
|
|
return dpiscale == DPI_600 ? IML_IMAGE_FLAG_S3|IML_IMAGE_FLAG_UHD
|
|
: get_i(dpiscale, 0, 0,
|
|
0, // DPI_100
|
|
IML_IMAGE_FLAG_QHD, // DPI_150
|
|
IML_IMAGE_FLAG_UHD, // DPI_200
|
|
IML_IMAGE_FLAG_UHD|IML_IMAGE_FLAG_QHD, // 'DPI_250' - not used
|
|
IML_IMAGE_FLAG_S3 // DPI_300
|
|
);
|
|
}
|
|
|
|
Iml::Iml(const char **name, int n)
|
|
: name(name)
|
|
{
|
|
premultiply = true;
|
|
Init(n);
|
|
}
|
|
|
|
void Iml::ResetAll()
|
|
{
|
|
for(int i = 0; i < GetImlCount(); i++)
|
|
GetIml(i).Reset();
|
|
}
|
|
|
|
void Iml::SkinAll()
|
|
{
|
|
for(int i = 0; i < GetImlCount(); i++)
|
|
GetIml(i).Skin();
|
|
}
|
|
|
|
static StaticCriticalSection sImgMapLock;
|
|
|
|
static VectorMap<String, Iml *>& sImgMap()
|
|
{
|
|
static VectorMap<String, Iml *> x;
|
|
return x;
|
|
}
|
|
|
|
void Register(const char *imageclass, Iml& list)
|
|
{
|
|
#ifdef flagCHECKINIT
|
|
RLOG("Registering iml " << imageclass);
|
|
#endif
|
|
INTERLOCKED_(sImgMapLock)
|
|
sImgMap().GetAdd(imageclass) = &list;
|
|
}
|
|
|
|
int GetImlCount()
|
|
{
|
|
int q = 0;
|
|
INTERLOCKED_(sImgMapLock)
|
|
q = sImgMap().GetCount();
|
|
return q;
|
|
}
|
|
|
|
Iml& GetIml(int i)
|
|
{
|
|
return *sImgMap()[i];
|
|
}
|
|
|
|
String GetImlName(int i)
|
|
{
|
|
Mutex::Lock __(sImlLock);
|
|
return sImgMap().GetKey(i);
|
|
}
|
|
|
|
int FindIml(const char *name)
|
|
{
|
|
Mutex::Lock __(sImlLock);
|
|
return sImgMap().Find(name);
|
|
}
|
|
|
|
Image GetImlImage(const char *name)
|
|
{
|
|
Image m;
|
|
const char *w = strchr(name, ':');
|
|
if(w) {
|
|
Mutex::Lock __(sImlLock);
|
|
int q = FindIml(String(name, w));
|
|
if(q >= 0) {
|
|
Iml& iml = *sImgMap()[q];
|
|
while(*w == ':')
|
|
w++;
|
|
q = iml.Find(w);
|
|
if(q >= 0)
|
|
m = iml.Get(q);
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
|
|
void SetImlImage(const char *name, const Image& m)
|
|
{
|
|
const char *w = strchr(name, ':');
|
|
if(w) {
|
|
Mutex::Lock __(sImlLock);
|
|
int q = FindIml(String(name, w));
|
|
if(q >= 0) {
|
|
Iml& iml = *sImgMap()[q];
|
|
while(*w == ':')
|
|
w++;
|
|
q = iml.Find(w);
|
|
if(q >= 0)
|
|
iml.Set(q, m);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|