diff --git a/Ide/src/MissingFiles.cpp b/Ide/src/MissingFiles.cpp --- a/Ide/src/MissingFiles.cpp +++ b/Ide/src/MissingFiles.cpp @@ -1,402 +1,403 @@ #include "lgi/common/Lgi.h" #include "lgi/common/FileSelect.h" +#include "lgi/common/Levenshtein.h" + #include "LgiIde.h" #include "ProjectNode.h" -#include "levenshtein.h" enum Msgs { M_CHECK_FILE = M_USER + 100, M_MISSING_FILE, M_ADD_SEARCH_PATH, M_SEARCH, M_RESULTS, M_RECURSE, }; struct SearchResults { ProjectNode *Node; LString Path; LString::Array Matches; SearchResults() { Node = 0; } }; class FileExistsThread : public LEventTargetThread { int Hnd; public: FileExistsThread(int hnd) : LEventTargetThread("FileExistsThread") { Hnd = hnd; } LMessage::Result OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_CHECK_FILE: { LAutoPtr Sr((SearchResults*)Msg->A()); bool e = LFileExists(Sr->Path); // printf("Checking '%s' = %i\n", Sr->Path.Get(), e); if (!e) PostObject(Hnd, M_MISSING_FILE, Sr); break; } } return 0; } }; bool IsParentFolder(LString p, LString c) { LString::Array d1 = p.Split(DIR_STR); LString::Array d2 = c.Split(DIR_STR); if (d1.Length() > d2.Length()) return false; for (unsigned i=0; i Search; LArray Files; public: SearchThread(int hnd) : LEventTargetThread("SearchThread") { Hnd = hnd; } ~SearchThread() { Files.DeleteArrays(); } LMessage::Result OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_ADD_SEARCH_PATH: { LAutoPtr p((LString*)Msg->A()); bool IsParent = false; for (unsigned i=0; iEquals(Search[i]) || (IsParent = IsParentFolder(Search[i], *p))) { printf("'%s' is parent of '%s'\n", Search[i].Get(), p->Get()); break; } } if (!IsParent) { printf("Adding '%s'\n", p->Get()); Search.New() = *p; } break; } case M_RECURSE: { for (unsigned i=0; i Ext; Ext.Add("*.h"); Ext.Add("*.hpp"); Ext.Add("*.c"); Ext.Add("*.cpp"); Ext.Add("*.mm"); // printf("Recursing '%s'\n", Search[i].Get()); LRecursiveFileSearch(Search[i], &Ext, &Files); } break; } case M_SEARCH: { LAutoPtr Sr((SearchResults*)Msg->A()); char *leaf1 = LGetLeaf(Sr->Path); for (unsigned i=0; iMatches.New() = Files[i]; } } printf("Posting '%s' with %i results.\n", Sr->Path.Get(), (int)Sr->Matches.Length()); PostObject(Hnd, M_RESULTS, Sr); break; } } return 0; } }; class MissingFiles : public LDialog { IdeProject *Proj; int SearchHnd; int ExistsHnd; LList *Lst; LArray Files; public: MissingFiles(IdeProject *proj) { Proj = proj; SearchHnd = -1; ExistsHnd = -1; Lst = NULL; SetParent(Proj->GetApp()); if (LoadFromResource(IDD_MISSING_FILES)) { GetViewById(IDC_RESULTS, Lst); MoveSameScreen(Proj->GetApp()); SetCtrlEnabled(IDC_MISSING, false); SetCtrlEnabled(IDC_RESULTS, false); SetCtrlEnabled(IDC_BROWSE, false); ExistsHnd = (new FileExistsThread(AddDispatch()))->GetHandle(); SearchHnd = (new SearchThread(AddDispatch()))->GetHandle(); LHashTbl,bool> Flds; List Child; Proj->GetChildProjects(Child); Child.Add(Proj); LArray Nodes; for (auto p: Child) { if (p->GetAllNodes(Nodes)) { for (auto Node: Nodes) { auto s = Node->GetFullPath(); if (s) { LString sOld = s.Get(); if (p->CheckExists(s) && Strcmp(sOld.Get(), s.Get()) != 0 && Stricmp(sOld.Get(), s.Get()) == 0) { // Case change? Node->SetFileName(s); } SearchResults *Sr = new SearchResults; Sr->Node = Node; Sr->Path = s; PostThreadEvent(ExistsHnd, M_CHECK_FILE, (LMessage::Param) Sr); LString Parent = s.Get(); LTrimDir(Parent); Flds.Add(Parent, true); } } } auto s = p->GetBasePath(); if (s) { LTrimDir(s); Flds.Add(s, true); } } for (auto i: Flds) PostThreadEvent(SearchHnd, M_ADD_SEARCH_PATH, (LMessage::Param) new LString(i.key)); PostThreadEvent(SearchHnd, M_RECURSE); } } ~MissingFiles() { if (SearchHnd >= 0) LEventSinkMap::Dispatch.CancelThread(SearchHnd); if (ExistsHnd >= 0) LEventSinkMap::Dispatch.CancelThread(ExistsHnd); } void OnReplace(const char *NewPath) { SearchResults *Sr = Files.First(); if (!Sr) return; printf("%s:%i - Setting node to '%s'\n", _FL, Sr->Path.Get()); Sr->Node->SetFileName(NewPath); Files.DeleteAt(0, true); delete Sr; OnFile(); } void OnDelete() { SearchResults *Sr = Files.First(); if (!Sr) return; bool Has = Proj->HasNode(Sr->Node); if (Has) Sr->Node->Delete(); Files.DeleteAt(0, true); if (Has) delete Sr; OnFile(); } void OnFile() { bool Has = Files.Length() > 0; SetCtrlEnabled(IDC_MISSING, Has); SetCtrlEnabled(IDC_BROWSE, Has); SetCtrlEnabled(IDC_RESULTS, Has); Lst->Empty(); if (Has) { SetCtrlName(IDC_MISSING, Files[0]->Path); for (unsigned i=0; iMatches.Length(); i++) { LListItem *li = new LListItem; li->SetText(Files[0]->Matches[i]); Lst->Insert(li); } Lst->ResizeColumnsToContent(); } else { SetCtrlName(IDC_MISSING, NULL); } } int OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_BROWSE: { LFileSelect *s = new LFileSelect; LAutoString Dir = Proj->GetBasePath(); s->Parent(this); s->InitialDir(Dir); s->Open([&](auto s, auto ok) { if (ok) OnReplace(s->Name()); delete s; }); break; } case IDC_DELETE: { OnDelete(); break; } case IDC_RESULTS: { switch (n.Type) { case LNotifyItemDoubleClick: { LListItem *li = Lst->GetSelected(); if (li) { OnReplace(li->GetText(0)); } break; } default: break; } break; } case IDOK: { EndModal(); break; } } return 0; } LMessage::Result OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_MISSING_FILE: { LAutoPtr Sr((SearchResults*)Msg->A()); if (Sr) { printf("Missing file '%s'\n", Sr->Path.Get()); PostThreadEvent(SearchHnd, M_SEARCH, (LMessage::Param) Sr.Release()); } break; } case M_RESULTS: { SearchResults *Sr = (SearchResults*)Msg->A(); bool HasNode = false; for (unsigned i=0; iNode == Sr->Node) { LgiTrace("%s:%i - Node already in Files.\n", _FL); HasNode = true; break; } } if (!HasNode) { Files.Add((SearchResults*)Msg->A()); OnFile(); } break; } } return LDialog::OnEvent(Msg); } }; void FixMissingFilesDlg(IdeProject *Proj) { auto Dlg = new MissingFiles(Proj); Dlg->DoModal(NULL); } diff --git a/Ide/win/LgiIde.vcxproj b/Ide/win/LgiIde.vcxproj --- a/Ide/win/LgiIde.vcxproj +++ b/Ide/win/LgiIde.vcxproj @@ -1,280 +1,278 @@  Debug Win32 Debug x64 Release Win32 Release x64 LgiIde {967AF7EF-7C17-4FCC-997E-BED17F886F07} LgiIde_vc8 Win32Proj 10.0 Application v142 Unicode true Application v142 Unicode true Application v142 Unicode Application v142 Unicode <_ProjectFileVersion>12.0.30501.0 $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ true $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26 $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ true $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26 $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ false $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26 $(Platform)$(Configuration)14\ $(Platform)$(Configuration)14\ false $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26 Disabled ..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;..\resources;C:\Program Files (x86)\OpenSSL-Win32\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL Level2 ProgramDatabase imm32.lib;Mincore.lib;%(AdditionalDependencies) $(OutDir)$(TargetFileName) ..\lib;%(AdditionalLibraryDirectories) true Windows false MachineX86 Disabled ..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;..\resources;C:\Program Files\OpenSSL-Win64\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;WINDOWS;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level2 ProgramDatabase imm32.lib;%(AdditionalDependencies) $(OutDir)$(TargetFileName) ..\lib;%(AdditionalLibraryDirectories) true Windows false ..\..\src\win\General\Lgi.manifest ..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;.\Resources;C:\Program Files (x86)\OpenSSL-Win32\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;WINDOWS;%(PreprocessorDefinitions) MultiThreadedDLL Level2 ProgramDatabase imm32.lib;Mincore.lib;%(AdditionalDependencies) $(OutDir)$(TargetFileName) ..\lib;%(AdditionalLibraryDirectories) true Windows true true false MachineX86 ..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;.\Resources;C:\Program Files\OpenSSL-Win64\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;WINDOWS;%(PreprocessorDefinitions) MultiThreadedDLL Level2 ProgramDatabase imm32.lib;%(AdditionalDependencies) $(OutDir)$(TargetFileName) ..\lib;%(AdditionalLibraryDirectories) true Windows true true false ..\..\src\win\General\Lgi.manifest - - {95df9ca4-6d37-4a85-a648-80c2712e0da1} false true false true \ No newline at end of file diff --git a/Ide/win/LgiIde.vcxproj.filters b/Ide/win/LgiIde.vcxproj.filters --- a/Ide/win/LgiIde.vcxproj.filters +++ b/Ide/win/LgiIde.vcxproj.filters @@ -1,267 +1,250 @@  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {6b83273a-b199-4420-ae65-9663c7c7e75b} {e809cc3d-4694-4e36-b2d4-60e9f9450e56} {b327086f-7d0e-4840-ac95-7dc18a89fe19} {3cefce7e-26bb-4e9e-8711-c7fa5c51373d} {63c6319b-3755-4b59-95f2-1e7b5b2c1a75} {f1667c53-311d-44b9-aafb-6db5c2bb7ea7} {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav {0823126b-5e9f-417c-b233-7025ea646dad} {2be01d30-b91b-466c-8aaf-bb656db55afb} {404b5cc0-5e53-4ae3-945c-274b0fc3f889} {29793244-2c02-4203-b755-49237ba42e24} - - {f793da4b-cf25-40f4-a90c-4a549d33d87e} - {a789d0ae-552d-4e98-902b-7ffb0d018bbc} {5aca11fa-b1cb-4707-bb7f-22f85d399ab9} {abcd6327-e6b7-4c09-8f78-c42a306904fe} Source Files\Debugger Lgi Source Files\Search Source Files\Search Source Files\Utils Source Files\Utils Source Files\Docs Source Files\Docs Source Files\Projects - - Source Files\levenshtein - Source Files Source Files\Search\Cpp Parser Source Files\Projects Source Files\Search\Python Parser Resource Files Resource Files Source Files\Scripting Source Files\Scripting Source Files\Search\Simple Cpp Parser Source Files\Utils Source Files\Process Source Files\Dialogs Source Files\Dialogs - - Source Files\Debugger - - - Source Files\Debugger - Lgi Source Files\Debugger Source Files\Debugger Source Files\Docs Source Files\Docs Source Files\Search Source Files\Search Source Files\Utils Source Files\Utils Source Files\Utils Lgi Source Files Source Files\Docs Source Files\Projects Source Files\Projects Source Files\Search\Javascript - - Source Files\levenshtein - Source Files\Scripting Source Files Lgi Lgi Lgi Source Files\Utils Source Files\Utils Source Files\Projects Lgi Source Files\Search\Cpp Parser Lgi Source Files\Projects Source Files\Search\Python Parser Source Files\Scripting Source Files\Scripting Source Files\Scripting Source Files\Search\Simple Cpp Parser Source Files\Utils Source Files\Utils Source Files\Dialogs Source Files\Dialogs - - Resource Files - + Resource Files - - Source Files\Scripting + + Source Files \ No newline at end of file diff --git a/include/lgi/common/Array.h b/include/lgi/common/Array.h --- a/include/lgi/common/Array.h +++ b/include/lgi/common/Array.h @@ -1,931 +1,935 @@ /// \file /// \author Matthew Allen /// \brief Growable, type-safe array. #pragma once #include #include #include #include #include #define GARRAY_MIN_SIZE 16 #if defined(LGI_CHECK_MALLOC) && !defined(LGI_MEM_DEBUG) #error "Include GMem.h first" #endif #if defined(PLATFORM_MINGW) #ifdef __cplusplus extern "C" { #endif #ifndef _QSORT_S_DEFINED #define _QSORT_S_DEFINED _CRTIMP void __cdecl qsort_s(void *_Base, size_t _NumOfElements, size_t _SizeOfElements, int (__cdecl *_PtFuncCompare)(void *,const void *,const void *), void *_Context); #endif #ifdef __cplusplus } #endif #endif #include "lgi/common/Range.h" /// \brief Growable type-safe array. /// \ingroup Base /// /// You can store simple objects inline in this array, but all their contents are initialized /// to the octet 0x00. Which limits use to objects that don't have a virtual table and /// don't need construction (constructors are not called). /// /// The objects are copied around during use so you can't assume their pointer /// will remain the same over time either. However when objects are deleted from the /// array their destructors WILL be called. This allows you to have simple objects that /// have dynamically allocated pointers that are freed properly. A good example of this /// type of object is the LVariant or LAutoString class. /// /// If you want to store objects with a virtual table, or that need their constructor /// to be called then you should create the LArray with pointers to the objects instead /// of inline objects. And to clean up the memory you can call LArray::DeleteObjects or /// LArray::DeleteArrays. template class LArray { - Type *p; - size_t len; - size_t alloc; + Type *p = NULL; + size_t len = 0; + size_t alloc = 0; #ifdef _DEBUG public: size_t GetAlloc() { return alloc; } #endif protected: - bool fixed = false; - bool warnResize = true; + uint8_t fixed : 1; + uint8_t warnResize : 1; public: typedef Type ItemType; /// Constructor LArray(size_t PreAlloc = 0) { - p = 0; alloc = len = PreAlloc; fixed = false; + warnResize = true; + if (alloc) { size_t Bytes = sizeof(Type) * alloc; p = (Type*) malloc(Bytes); if (p) { memset(p, 0, Bytes); } else { alloc = len = 0; } } } LArray(std::initializer_list il) { - p = NULL; - alloc = len = 0; fixed = false; + warnResize = true; + if (Length(il.size())) { size_t n = 0; for (auto i: il) p[n++] = i; } } LArray(const LArray &c) { - p = 0; - alloc = len = 0; fixed = false; + warnResize = true; + *this = c; } /// Destructor ~LArray() { Length(0); } /// This frees the memory without calling the destructor of the elements. /// Useful if you're storing large amounts of plain data types, like char or int. void Free() { if (p) { free(p); p = NULL; } len = alloc = 0; } /// Does a range check on a pointer... /// \returns true if the pointer is pointing to a valid object /// in this array. bool PtrCheck(void *Ptr) { return p != NULL && Ptr >= p && Ptr < &p[len]; } /// Does a range check on an index... bool IdxCheck(ssize_t i) const { return i >= 0 && i < (ssize_t)len; } /// Returns the number of used entries size_t Length() const { return len; } /// Makes the length fixed.. void SetFixedLength(bool fix = true, bool warn = true) { fixed = fix; warnResize = warn; } /// Emtpies the array of all objects. bool Empty() { return Length(0); } /// Sets the length of available entries bool Length(size_t i) { if (i > 0) { if (i > len && fixed) { assert(!"Attempt to enlarged fixed array."); return false; } size_t nalloc = alloc; if (i < len) { // Shrinking } else { // Expanding nalloc = 0x10; while (nalloc < i) nalloc <<= 1; assert(nalloc >= i); } if (nalloc != alloc) { Type *np = (Type*)malloc(sizeof(Type) * nalloc); if (!np) { return false; } memset(np + len, 0, (nalloc - len) * sizeof(Type)); if (p) { // copy across common elements memcpy(np, p, MIN(len, i) * sizeof(Type)); free(p); } p = np; alloc = nalloc; } if (i > len) { // zero new elements memset(p + len, 0, sizeof(Type) * (nalloc - len)); } else if (i < len) { for (size_t n=i; n &a) const { if (Length() != a.Length()) return false; for (size_t i=0; i &a) const { return !(*this == a); } LArray &operator =(const LArray &a) { Length(a.Length()); + fixed = a.fixed; + warnResize = a.warnResize; if (p && a.p) { for (size_t i=0; i= 0 && (uint32_t)i < len) return p[i]; static Type t; return t; } // Returns the address of an item or NULL if index is out of range Type *AddressOf(size_t i = 0) { return i < len ? p + i : NULL; } /// \brief Returns a reference a given entry. /// /// If the entry is off the end of the array and "fixed" is false, /// it will grow to make it valid. Type &operator [](size_t i) { static Type t; if ( (fixed && (uint32_t)i >= len) ) { if (warnResize) assert(!"Attempt to enlarged fixed array."); return t; } if (i >= (int)alloc) { // increase array length size_t nalloc = MAX(alloc, GARRAY_MIN_SIZE); while (nalloc <= (uint32_t)i) { nalloc <<= 1; } #if 0 if (nalloc > 1<<30) { #if defined(_DEBUG) && defined(_MSC_VER) LAssert(0); #endif ZeroObj(t); return t; } #endif // alloc new array Type *np = (Type*) malloc(sizeof(Type) * nalloc); if (np) { // clear new cells memset(np + len, 0, (nalloc - len) * sizeof(Type)); if (p) { // copy across old cells memcpy(np, p, len * sizeof(Type)); // clear old array free(p); } // new values p = np; alloc = nalloc; } else { static Type *t = 0; return *t; } } // adjust length of the the array if ((uint32_t)i + 1 > len) { len = i + 1; } return p[i]; } /// Delete all the entries as if they are pointers to objects void DeleteObjects() { if (len > 0) { size_t InitialLen = len; delete p[0]; if (InitialLen == len) { // Non self deleting for (uint i=1; i= 0; } /// Removes last element bool PopLast() { if (len <= 0) return false; return Length(len - 1); } /// Deletes an entry bool DeleteAt ( /// The index of the entry to delete size_t Index, /// true if the order of the array matters, otherwise false. bool Ordered = false ) { if (p && Index < len) { // Delete the object p[Index].~Type(); // Move the memory up if (Index < len - 1) { if (Ordered) { memmove(p + Index, p + Index + 1, (len - Index - 1) * sizeof(Type) ); } else { p[Index] = p[len-1]; } } // Adjust length len--; // Kill the element at the end... otherwise New() returns non-zero data. memset(p + len, 0, sizeof(Type)); return true; } return false; } /// Deletes a range. /// This operation always maintains order. /// \returns the number of removed elements ssize_t DeleteRange(LRange r) { // Truncate range to the size of this array if (r.Start < 0) r.Start = 0; if (r.End() >= (ssize_t)len) r.Len = len - r.Start; // Delete all the elements we are removing for (ssize_t i=r.Start; i 0) memmove(&p[r.Start], &p[r.End()], sizeof(Type)*Remain); len = r.Start + Remain; return r.Len; } /// Deletes the entry 'n' bool Delete ( /// The value of the entry to delete Type n, /// true if the order of the array matters, otherwise false. bool Ordered = false ) { ssize_t i = IndexOf(n); if (p && i >= 0) { return DeleteAt(i, Ordered); } return false; } /// Appends an element void Add ( /// Item to insert const Type &n ) { (*this)[len] = n; } /// Appends multiple elements bool Add ( /// Items to insert const Type *s, /// Length of array ssize_t count ) { if (!s || count < 1) return false; ssize_t i = len; if (!Length(len + count)) return false; Type *d = p + i; while (count--) { *d++ = *s++; } return true; } /// Appends an array of elements bool Add ( /// Array to insert const LArray &a ) { ssize_t old = len; if (Length(len + a.Length())) { for (unsigned i=0; i operator +(const LArray &b) { LArray a = *this; a.Add(b); return a; } LArray &operator +=(const LArray &b) { Add(b); return *this; } /// Inserts an element into the array bool AddAt ( /// Item to insert before size_t Index, /// Item to insert Type n ) { // Make room if (!Length(len + 1)) return false; if (Index < len - 1) // Shift elements after insert point up one memmove(p + Index + 1, p + Index, (len - Index - 1) * sizeof(Type) ); else // Add at the end, not after the end... Index = len - 1; // Insert item memset(p + Index, 0, sizeof(*p)); p[Index] = n; return true; } /// Inserts an array into the array at a position bool AddAt ( /// Item to insert before size_t Index, /// Array to insert const LArray &a ) { // Make room if (!Length(len + a.Length())) return false; if (Index < len - 1) // Shift elements after insert point up one memmove(p + Index + a.Length(), p + Index, (len - Index - a.Length()) * sizeof(Type) ); else // Add at the end, not after the end... Index = len - 1; // Insert items memset(p + Index, 0, a.Length() * sizeof(*p)); for (size_t i=0; i 0) return 1; return r < 0 ? -1 : 0; }); } /// Sorts the array via a callback. void Sort(std::function Compare) { #if !defined(WINDOWS) && !defined(HAIKU) && !defined(LINUX) #define USER_DATA_FIRST 1 #else #define USER_DATA_FIRST 0 #endif #if defined(WINDOWS) /* _ACRTIMP void __cdecl qsort_s(void* _Base, rsize_t _NumOfElements, rsize_t _SizeOfElements, int (__cdecl* _PtFuncCompare)(void*, void const*, void const*), void* _Context); */ qsort_s #else qsort_r #endif ( p, len, sizeof(Type), #if USER_DATA_FIRST &Compare, #endif #if defined(HAIKU) || defined(LINUX) // typedef int (*_compare_function_qsort_r)(const void*, const void*, void*); // extern void qsort_r(void* base, size_t numElements, size_t sizeOfElement, _compare_function_qsort_r, void* cookie); [](const void *a, const void *b, void *ud) -> int #else [](void *ud, const void *a, const void *b) -> int #endif { return (*( (std::function*)ud ))((Type*)a, (Type*)b); } #if !USER_DATA_FIRST , &Compare #endif ); } /// \returns a reference to a new object on the end of the array /// /// Never assign this to an existing variable. e.g: /// LArray a; /// MyObject &o = a.New(); /// o.Type = something; /// o = a.New(); /// o.Type = something else; /// /// This causes the first object to be overwritten with a blank copy. Type &New() { return (*this)[len]; } /// Returns the memory held by the array and sets itself to empty Type *Release() { Type *Ptr = p; p = 0; len = alloc = 0; return Ptr; } void Swap(LArray &other) { LSwap(p, other.p); LSwap(len, other.len); LSwap(alloc, other.alloc); } /// Swaps a range of elements between this array and 'b' bool SwapRange ( // The range of 'this' to swap out LRange aRange, // The other array to swap with LArray &b, // The range of 'b' to swap with this array LRange bRange ) { LArray Tmp; // Store entries in this that will be swapped Tmp.Add(AddressOf(aRange.Start), aRange.Len); // Copy b's range into this ssize_t Common = MIN(bRange.Len, aRange.Len); ssize_t aIdx = aRange.Start; ssize_t bIdx = bRange.Start; for (int i=0; i bRange.Len) { // Shrink the range in this to fit 'b' ssize_t Del = aRange.Len - bRange.Len; for (ssize_t i=0; i bRange.Len) { // Grow range to fit this ssize_t Add = aRange.Len - bRange.Len; for (ssize_t i=0; i class Iter { friend class ::LArray; ssize_t i; char each_dir; LArray *a; public: Iter(LArray *arr) // 'End' constructor { i = -1; a = arr; each_dir = 0; } Iter(LArray *arr, size_t pos) { i = pos; a = arr; each_dir = 0; } bool operator ==(const Iter &it) const { int x = (int)In() + (int)it.In(); if (x == 2) return (a == it.a) && (i == it.i); return x == 0; } bool operator !=(const Iter &it) const { return !(*this == it); } operator bool() const { return In(); } bool In() const { return i >= 0 && i < (ssize_t)a->Length(); } bool End() const { return i < 0 || i >= a->Length(); } T &operator *() { return (*a)[i]; } Iter &operator ++() { i++; return *this; } Iter &operator --() { i--; return *this; } Iter &operator ++(int) { i++; return *this; } Iter &operator --(int) { i--; return *this; } }; typedef Iter I; I begin(size_t start = 0) { return I(this, start); } I rbegin() { return I(this, len-1); } I end() { return I(this); } bool Delete(I &It) { return DeleteAt(It.i, true); } LArray Slice(ssize_t Start, ssize_t End = -1) { LArray a; // Range limit the inputs... if (Start < 0) Start = len + Start; if (Start > (ssize_t)len) Start = len; if (End < 0) End = len + End + 1; if (End > (ssize_t)len) End = len; if (End > Start) { a.Length(End - Start); for (size_t i=0; i Reverse() { LArray r; r.Length(len); for (size_t i=0, k=len-1; i Compare) { static Type Empty; if (!Compare) return Empty; for (size_t s = 0, e = len - 1; s <= e; ) { if (e - s < 2) { if (Compare(p[s]) == 0) return p[s]; if (e > s && Compare(p[e]) == 0) return p[e]; break; // Not found } size_t mid = s + ((e - s) >> 1); int result = Compare(p[mid]); if (result == 0) return p[mid]; if (result < 0) s = mid + 1; // search after the mid point else e = mid - 1; // search before } return Empty; } }; diff --git a/include/lgi/common/StringClass.h b/include/lgi/common/StringClass.h --- a/include/lgi/common/StringClass.h +++ b/include/lgi/common/StringClass.h @@ -1,1255 +1,1254 @@ /* * A mis-guided attempt to make a string class look and feel like a python string. * * Author: Matthew Allen * Email: fret@memecode.com * Created: 16 Sept 2014 */ #pragma once #include #include #include #if defined(_MSC_VER) || defined(__GTK_H__) // This fixes compile errors in VS2008/Gtk #undef _SIGN_DEFINED #undef abs #endif #include #if defined(_MSC_VER) && _MSC_VER < 1800/*_MSC_VER_VS2013*/ #include #define PRId64 "I64i" #else #define __STDC_FORMAT_MACROS 1 #include #include #ifndef PRId64 #warning "PRId64 not defined." #define PRId64 "Ld" #endif #endif #include "LgiOsDefs.h" #include "lgi/common/Unicode.h" #include "lgi/common/Array.h" #ifndef IsDigit #define IsDigit(ch) ((ch) >= '0' && (ch) <= '9') #endif LgiExtern int LPrintf(class LString &Str, const char *Format, va_list &Arg); /// A pythonic string class. class LString { protected: /// This structure holds the string's data itself and is shared /// between one or more LString instances. struct RefStr { /// A reference count int32 Refs; /// The bytes in 'Str' not including the NULL terminator size_t Len; /// The first byte of the string. Further bytes are allocated /// off the end of the structure using malloc. This must always /// be the last element in the struct. char Str[1]; } *Str; inline void _strip(LString &ret, const char *set, bool left, bool right) { if (!Str) return; char *s = Str->Str; char *e = s + Str->Len; if (!set) set = " \t\r\n"; if (left) { while (s < e && strchr(set, *s)) s++; } if (right) { while (e > s && strchr(set, e[-1])) e--; } if (e > s) ret.Set(s, e - s); } public: #ifdef LGI_UNIT_TESTS static int32 RefStrCount; #endif /// A copyable array of strings class Array : public LArray { public: Array(size_t PreAlloc = 0) : LArray(PreAlloc) { // This allows the parent array to return an empty // string without asserting if the caller requests an // out of range index warnResize = false; } Array(const Array &a) { *this = (Array&)a; } Array &operator =(const Array &a) { - SetFixedLength(false); *((LArray*)this) = a; - SetFixedLength(true); + SetFixedLength(true, false); return *this; } Array &operator +=(const Array &a) { SetFixedLength(false); Add(a); SetFixedLength(true); return *this; } Array &operator +=(const LArray &a) { SetFixedLength(false); Add(a); SetFixedLength(true); return *this; } }; /// Empty constructor LString() { Str = NULL; } // This odd looking constructor allows the object to be used as the value type // in a GHashTable, where the initialiser is '0', an integer. LString(int i) { Str = NULL; } #ifndef _MSC_VER // This odd looking constructor allows the object to be used as the value type // in a GHashTable, where the initialiser is '0', an integer. LString(long int i) { Str = NULL; } #endif /// String constructor LString(const char *str, ptrdiff_t bytes) { Str = NULL; Set(str, bytes); } /// const char* constructor LString(const char *str) { Str = NULL; Set(str); } /// const char16* constructor LString(const wchar_t *str, ptrdiff_t wchars = -1) { Str = NULL; SetW(str, wchars); } #if defined(_WIN32) || defined(MAC) /// const uint32* constructor LString(const uint32_t *str, ptrdiff_t chars = -1) { Str = NULL; if (chars < 0) chars = Strlen(str); ptrdiff_t utf_len = 0; const uint32_t *end = str + chars; const uint32_t *c = str; while (c < end) { uint8_t utf[6], *u = utf; ssize_t len = sizeof(utf); if (!LgiUtf32To8(*c++, u, len)) break; utf_len += u - utf; } if (Length((uint32_t)utf_len)) { c = str; uint8_t *u = (uint8_t*)Str->Str; ssize_t len = Str->Len; while (c < end) { if (!LgiUtf32To8(*c++, u, len)) break; } *u++ = 0; } } #endif /// LString constructor LString(const LString &s) { Str = s.Str; if (Str) Str->Refs++; } ~LString() { Empty(); } /// Removes a reference to the string and deletes if needed void Empty() { if (!Str) return; Str->Refs--; if (Str->Refs < 0) { assert(!"Invalid refs"); } if (Str->Refs == 0) { free(Str); #ifdef LGI_UNIT_TESTS RefStrCount--; #endif } Str = NULL; } /// Returns the pointer to the string data char *Get() const { return Str ? Str->Str : NULL; } /// Sets the string to a new value bool Set ( /// Can be a pointer to string data or NULL to create an empty buffer (requires valid length) const char *str, /// Byte length of input string or -1 to copy till the NULL terminator. ptrdiff_t bytes = -1 ) { Empty(); if (bytes < 0) { if (str) bytes = strlen(str); else return false; } Str = (RefStr*)malloc(sizeof(RefStr) + bytes); if (!Str) return false; Str->Refs = 1; Str->Len = (uint32_t)bytes; #ifdef LGI_UNIT_TESTS RefStrCount++; #endif if (str) memcpy(Str->Str, str, bytes); Str->Str[bytes] = 0; return true; } /// Sets the string to a new value bool SetW ( /// Can be a pointer to string data or NULL to create an empty buffer (requires valid length) const wchar_t *str, /// Number of 'char16' values in the input string or -1 to copy till the NULL terminator. ptrdiff_t wchars = -1 ) { size_t Sz = WideToUtf8Len(str, wchars); if (Length(Sz)) { #ifdef _MSC_VER const uint16 *i = (const uint16*) str; ssize_t InLen = wchars >= 0 ? wchars << 1 : 0x7fffffff; assert(sizeof(*i) == sizeof(*str)); uint8_t *o = (uint8_t*)Str->Str; ssize_t OutLen = Str->Len; for (uint32_t ch; ch = LgiUtf16To32(i, InLen); ) { if (!LgiUtf32To8(ch, o, OutLen)) { *o = 0; break; } } #else uint8_t *o = (uint8_t*)Str->Str; ssize_t OutLen = Str->Len; if (wchars >= 0) { const wchar_t *end = str + wchars; for (const wchar_t *ch = str; ch < end; ch++) { if (!LgiUtf32To8(*ch, o, OutLen)) { *o = 0; break; } } } else { for (const wchar_t *ch = str; *ch; ch++) { if (!LgiUtf32To8(*ch, o, OutLen)) { *o = 0; break; } } } #endif *o = 0; } return true; } /// Equality operator (case sensitive) bool operator ==(const LString &s) { const char *a = Get(); const char *b = s.Get(); if (!a && !b) return true; if (!a || !b) return false; return !strcmp(a, b); } bool operator !=(const LString &s) { return !(*this == s); } // Equality function (default: case insensitive, as the operator== is case sensitive) bool Equals(const char *b, bool CaseInsensitive = true) const { const char *a = Get(); if (!a && !b) return true; if (!a || !b) return false; return !(CaseInsensitive ? _stricmp(a, b) : strcmp(a, b)); } // Equality function (default: case insensitive, as the operator== is case sensitive) bool Equals(const LString &s, bool CaseInsensitive = true) const { const char *a = Get(); const char *b = s.Get(); if (!a && !b) return true; if (!a || !b) return false; return !(CaseInsensitive ? _stricmp(a, b) : strcmp(a, b)); } /// Assignment operator to copy one string to another LString &operator =(const LString &s) { if (this != &s) { Empty(); Str = s.Str; if (Str) Str->Refs++; } return *this; } /// Equality with a C string (case sensitive) bool operator ==(const char *b) { const char *a = Get(); if (!a && !b) return true; if (!a || !b) return false; return !strcmp(a, b); } bool operator !=(const char *b) { return !(*this == b); } /// Assignment operators LString &operator =(const char *s) { if (Str == NULL || s < Str->Str || s > Str->Str + Str->Len) { Empty(); Set(s); } else if (s != Str->Str) { // Special case for setting it to part of itself // If you try and set a string to the start, it's a NOP ptrdiff_t Off = s - Str->Str; memmove(Str->Str, s, Str->Len - Off + 1); Str->Len -= (uint32_t)Off; } return *this; } LString &operator =(const wchar_t *s) { SetW(s); return *this; } LString &operator =(int val) { char n[32]; sprintf_s(n, sizeof(n), "%i", val); Set(n); return *this; } LString &operator =(int64 val) { char n[32]; sprintf_s(n, sizeof(n), "%" PRId64, (int64_t)val); Set(n); return *this; } /// Cast to C string operator operator char *() const { return Str && Str->Len > 0 ? Str->Str : NULL; } int operator -(const LString &s) const { return Stricmp(Get(), s.Get()); } /// Concatenation operator LString operator +(const LString &s) { LString Ret; size_t Len = Length() + s.Length(); if (Ret.Set(NULL, Len)) { char *p = Ret.Get(); if (p) { if (Str) { memcpy(p, Str->Str, Str->Len); p += Str->Len; } if (s.Str) { memcpy(p, s.Str->Str, s.Str->Len); p += s.Str->Len; } *p++ = 0; } } return Ret; } /// Concatenation / assignment operator LString &operator +=(const LString &s) { ssize_t Len = Length() + s.Length(); ssize_t Alloc = sizeof(RefStr) + Len; RefStr *rs = (RefStr*)malloc(Alloc); if (rs) { rs->Refs = 1; rs->Len = Len; #ifdef LGI_UNIT_TESTS RefStrCount++; #endif char *p = rs->Str; if (Str) { memcpy(p, Str->Str, Str->Len); p += Str->Len; } if (s.Str) { memcpy(p, s.Str->Str, s.Str->Len); p += s.Str->Len; } *p++ = 0; assert(p - (char*)rs <= Alloc); Empty(); Str = rs; } return *this; } LString operator *(ssize_t mul) { LString s; if (Str) { s.Length(Str->Len * mul); char *out = s.Get(); for (ssize_t i=0; iLen) memcpy(out, Str->Str, Str->Len); *out = 0; } return s; } /// Gets the length in bytes size_t Length() const { return Str ? Str->Len : 0; } size_t Length(ssize_t NewLen) { if (NewLen < 0) { LAssert(!"No negative string len."); Empty(); } else if (Str) { if (NewLen <= (ssize_t)Str->Len) { Str->Len = NewLen; Str->Str[NewLen] = 0; } else { RefStr *n = (RefStr*)malloc(sizeof(RefStr) + NewLen); if (n) { n->Len = NewLen; n->Refs = 1; memcpy(n->Str, Str->Str, Str->Len); n->Str[Str->Len] = 0; // NULL terminate... Empty(); // Deref the old string... Str = n; } else return 0; } } else { Str = (RefStr*)malloc(sizeof(RefStr) + NewLen); if (Str) { Str->Len = NewLen; Str->Refs = 1; Str->Str[0] = 0; // NULL terminate... } else return 0; } return Str ? Str->Len : 0; } /// Splits the string into parts using a separator Array Split(const char *Sep, int Count = -1, bool CaseSen = false) { Array a; if (Str && Sep) { const char *s = Str->Str, *Prev = s; const char *end = s + Str->Len; size_t SepLen = strlen(Sep); if (s[Str->Len] == 0) { while ((s = CaseSen ? strstr(s, Sep) : Stristr(s, Sep))) { if (s > Prev) a.New().Set(Prev, s - Prev); s += SepLen; Prev = s; if (Count > 0 && a.Length() >= (uint32_t)Count) break; } if (Prev < end) a.New().Set(Prev, end - Prev); a.SetFixedLength(); } else assert(!"String not NULL terminated."); } return a; } /// Splits the string into parts using a separator Array RSplit(const char *Sep, int Count = -1, bool CaseSen = false) { Array a; if (Str && Sep) { const char *s = Get(); size_t SepLen = strlen(Sep); LArray seps; while ((s = CaseSen ? strstr(s, Sep) : Stristr(s, Sep))) { seps.Add(s); s += SepLen; } ssize_t i, Last = seps.Length() - 1; LString p; for (i=Last; i>=0; i--) { const char *part = seps[i] + SepLen; if (i == Last) p.Set(part); else p.Set(part, seps[i+1]-part); a.AddAt(0, p); if (Count > 0 && a.Length() >= (uint32_t)Count) break; } const char *End = seps[i > 0 ? i : 0]; p.Set(Get(), End - Get()); a.AddAt(0, p); } a.SetFixedLength(true, false); return a; } /// Splits the string into parts using delimiter chars Array SplitDelimit(const char *Delimiters = NULL, int Count = -1, bool GroupDelimiters = true) const { Array a; if (Str) { const char *delim = Delimiters ? Delimiters : " \t\r\n"; const char *s = Get(), *end = s + Length(); while (s < end) { // Skip over non-delimiters const char *e = s; while (e < end && !strchr(delim, *e)) e++; if (e > s || !GroupDelimiters) a.New().Set(s, e - s); s = e; if (*s) s++; if (GroupDelimiters) { // Skip any delimiters while (s < end && strchr(delim, *s)) s++; } // Create the string if (Count > 0 && a.Length() >= (uint32_t)Count) break; } if ( s < end || ( !GroupDelimiters && s > Get() && strchr(delim, s[-1]) ) ) a.New().Set(s); } a.SetFixedLength(true, false); return a; } /// Joins an array of strings using a separator LString Join(const LArray &a) { LString ret; if (a.Length() == 0) return ret; char *Sep = Get(); size_t SepLen = Sep ? strlen(Sep) : 0; size_t Bytes = SepLen * (a.Length() - 1); LArray ALen; for (unsigned i=0; iStr; LArray Matches; while ((Match = (CaseSen ? strstr(Match, Old) : Stristr(Match, Old)))) { Matches.Add(Match); if (Count >= 0 && (int)Matches.Length() >= Count) break; Match += OldLen; } size_t NewSize = Str->Len + (Matches.Length() * (NewLen - OldLen)); s.Length((uint32_t)NewSize); char *Out = s.Get(); char *In = Str->Str; // For each match... for (unsigned i=0; iStr + Str->Len; if (In < End) { ptrdiff_t Bytes = End - In; memcpy(Out, In, Bytes); Out += Bytes; } assert(Out - s.Get() == NewSize); // Check we got the size right... *Out = 0; // Null terminate } else { s = *this; } return s; } /// Convert string to double double Float() { return Str ? atof(Str->Str) : NAN; } /// Convert to integer int64 Int(int Base = 10) { if (!Str) return -1; if ( Str->Len > 2 && Str->Str[0] == '0' && ( Str->Str[1] == 'x' || Str->Str[1] == 'X' ) ) { return Atoi(Str->Str+2, 16); } return Atoi(Str->Str, Base); } /// Checks if the string is a number bool IsNumeric() { if (!Str) return false; for (char *s = Str->Str; *s; s++) { if (!IsDigit(*s) && !strchr("e-+.", *s)) return false; } return true; } /// Check for non whitespace bool IsEmpty() { if (!Str) return true; for (char *s = Str->Str; *s; s++) { if (*s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') return false; } return true; } /// Reverses all the characters in the string LString Reverse() { LString s; if (Length() > 0) { s = Str->Str; for (auto *a = s.Get(), *b = s.Get() + s.Length() - 1; a < b; a++, b--) { char t = *a; *a = *b; *b = t; } } return s; } /// Find a sub-string ssize_t Find(const char *needle, ssize_t start = 0, ssize_t end = -1) { if (!needle) return -1; char *c = Get(); if (!c) return -1; char *pos = c + start; while (c < pos) { if (!*c) return -1; c++; } char *found = (end > 0) ? Strnstr(c, needle, end - start) : strstr(c, needle); return (found) ? found - Get() : -1; } /// Reverse find a string (starting from the end) ssize_t RFind(const char *needle, int start = 0, ssize_t end = -1) { if (!needle) return -1; char *c = Get(); if (!c) return -1; char *pos = c + start; while (c < pos) { if (!*c) return -1; c++; } char *found, *prev = NULL; size_t str_len = strlen(needle); while (( found = ( (end > 0) ? Strnstr(c, needle, end - start) : strstr(c, needle) ) )) { prev = found; c = found + str_len; } return (prev) ? prev - Get() : -1; } /// Returns a copy of the string with all the characters converted to lower case LString Lower() { LString s; if (Str && s.Set(Str->Str, Str->Len)) Strlwr(s.Get()); return s; } /// Returns a copy of the string with all the characters converted to upper case LString Upper() { LString s; if (Str && s.Set(Str->Str, Str->Len)) Strupr(s.Get()); return s; } void Swap(LString &s) { LSwap(Str, s.Str); } /// Gets the character at 'index' int operator() (ssize_t index) const { if (!Str) return 0; char *c = Str->Str; if (index < 0) { size_t idx = Str->Len + index; return c[idx]; } else if (index < (int)Str->Len) { return c[index]; } return 0; } /// Gets the string between at 'start' and 'end' (not including the end'th character) LString operator() (ptrdiff_t start, ptrdiff_t end) { LString s; if (Str) { ptrdiff_t start_idx = start < 0 ? Str->Len + start + 1 : start; if (start_idx >= 0 && (uint32_t)start_idx < Str->Len) { ptrdiff_t end_idx = end < 0 ? Str->Len + end + 1 : end; if (end_idx >= start_idx && (uint32_t)end_idx <= Str->Len) s.Set(Str->Str + start_idx, end_idx - start_idx); } } return s; } /// Strip off any leading and trailing characters from 'set' (or whitespace if NULL) LString Strip(const char *set = NULL) { LString ret; _strip(ret, set, true, true); return ret; } /// Strip off any leading characters from 'set' (or whitespace if NULL) LString LStrip(const char *set = NULL) { LString ret; _strip(ret, set, true, false); return ret; } /// Strip off any trailing characters from 'set' (or whitespace if NULL) LString RStrip(const char *set = NULL) { LString ret; _strip(ret, set, false, true); return ret; } /// Prints a formatted string to this object int Printf(const char *Fmt, ...) { Empty(); va_list Arg; va_start(Arg, Fmt); int Bytes = Printf(Arg, Fmt); va_end(Arg); return Bytes; } /// Prints a varargs string int Printf(va_list &Arg, const char *Fmt) { Empty(); return LPrintf(*this, Fmt, Arg); } static LString Escape(const char *In, ssize_t Len = -1, const char *Chars = "\r\n\b\\\'\"") { LString s; if (In && Chars) { char Buf[256]; int Ch = 0; if (Len < 0) Len = strlen(In); while (Len-- > 0) { if (Ch > sizeof(Buf)-4) { // Buffer full, add substring to 's' Buf[Ch] = 0; s += Buf; Ch = 0; } if (strchr(Chars, *In)) { Buf[Ch++] = '\\'; switch (*In) { #undef EscChar #define EscChar(from, to) \ case from: Buf[Ch++] = to; break EscChar('\n', 'n'); EscChar('\r', 'r'); EscChar('\\', '\\'); EscChar('\b', 'b'); EscChar('\a', 'a'); EscChar('\t', 't'); EscChar('\v', 'v'); EscChar('\'', '\''); EscChar('\"', '\"'); EscChar('&', '&'); EscChar('?', '?'); #undef EscChar default: Ch += sprintf_s(Buf+Ch, sizeof(Buf)-Ch, "x%02x", *In); break; } } else Buf[Ch++] = *In; In++; } if (Ch > 0) { Buf[Ch] = 0; s += Buf; } } return s; } template static LString UnEscape(const T *In, ssize_t Len = -1) { if (!In) return LString(); LString s; if (Len < 0) // As memory allocation/copying around data is far slower then // just scanning the string for size... don't try and chunk the // processing. Len = Strlen(In); if (!s.Length(Len)) return LString(); auto *Out = s.Get(); auto *End = In + Len; while (In < End) { if (*In == '\\') { In++; switch (*In) { case 'n': case 'N': *Out++ = '\n'; break; case 'r': case 'R': *Out++ = '\r'; break; case 'b': case 'B': *Out++ = '\b'; break; case 't': case 'T': *Out++ = '\t'; break; default: *Out++ = *In; break; case 0: break; } if (*In) In++; else break; } else *Out++ = *In++; } // Trim excess size off string s.Length(Out - s.Get()); return s; } static LString UnEscape(LString s) { return UnEscape(s.Get(), s.Length()); } #if defined(__GTK_H__) #elif defined(MAC) // && __COREFOUNDATION_CFBASE__ LString(const CFStringRef r) { Str = NULL; *this = r; } LString &operator =(CFStringRef r) { if (r) { CFIndex length = CFStringGetLength(r); CFRange range = CFRangeMake(0, length); CFIndex usedBufLen = 0; CFIndex slen = CFStringGetBytes(r, range, kCFStringEncodingUTF8, '?', false, NULL, 0, &usedBufLen); if (Set(NULL, usedBufLen)) { slen = CFStringGetBytes( r, range, kCFStringEncodingUTF8, '?', false, (UInt8*)Str->Str, Str->Len, &usedBufLen); Str->Str[usedBufLen] = 0; // NULL terminate } } return *this; } CFStringRef CreateStringRef() { char *s = Get(); if (!s) return NULL; return CFStringCreateWithCString(kCFAllocatorDefault, s, kCFStringEncodingUTF8); } #ifdef __OBJC__ NSString *NsStr() { if (Str) return [[NSString alloc] initWithBytes:Str->Str length:Str->Len encoding:NSUTF8StringEncoding]; return nil; } bool operator=(NSString *const s) { *this = [s UTF8String]; return !IsEmpty(); } LString(NSString *const s) { Str = NULL; *this = [s UTF8String]; } #endif #endif }; diff --git a/src/common/Gdc2/Filters/Png.cpp b/src/common/Gdc2/Filters/Png.cpp --- a/src/common/Gdc2/Filters/Png.cpp +++ b/src/common/Gdc2/Filters/Png.cpp @@ -1,1602 +1,1603 @@ + /*hdr ** FILE: Png.cpp ** AUTHOR: Matthew Allen ** DATE: 8/9/1998 ** DESCRIPTION: Png file filter ** ** Copyright (C) 2002, Matthew Allen ** fret@memecode.com */ // // 'png.h' comes from libpng, which you can get from: // http://www.libpng.org/pub/png/libpng.html // // You will also need zlib, which pnglib requires. zlib // is available here: // http://www.gzip.org/zlib // // If you don't want to build with PNG support then set // the define HAS_LIBPNG_ZLIB to '0' in Lgi.h // #ifndef __CYGWIN__ #include "math.h" #include "png.h" #endif #include "lgi/common/Lgi.h" #include "lgi/common/Palette.h" #ifdef __CYGWIN__ #include "png.h" #endif #include #include #include #include "lgi/common/LibraryUtils.h" #ifdef FILTER_UI #include "TransparentDlg.h" #endif #include "lgi/common/Variant.h" // Pixel formats typedef uint8_t Png8; typedef LRgb24 Png24; typedef LRgba32 Png32; typedef LRgb48 Png48; typedef LRgba64 Png64; #if PNG_LIBPNG_VER_MAJOR <= 1 && PNG_LIBPNG_VER_MINOR <= 2 #define png_const_infop png_infop #define png_const_bytep png_bytep #endif #ifdef LINUX const char *LinuxLibName() { static char lib[64]; sprintf_s(lib, sizeof(lib), "libpng%i", PNG_LIBPNG_VER_SONUM); // printf("png lib name = '%s'\n", lib); return lib; } #endif #if LIBPNG_SHARED #define LIBPNG Lib-> const char *sLibrary = #if defined(MAC) || defined(HAIKU) "libpng16" #elif defined(LINUX) LinuxLibName() #else #if defined(__CYGWIN__) "cygpng12" #else "libpng16" #ifdef _MSC_VER_STR "_" #if _MSC_VER >= _MSC_VER_VS2019 _MSC_YEAR_STR #else _MSC_VER_STR #endif #if defined(LGI_64BIT) "x64" #else "x32" #endif #ifdef _DEBUG "d" #endif #endif #endif #endif ; // Library interface class LibPng : public LLibrary { public: LibPng() : LLibrary(sLibrary) { static bool First = true; if (First) { First = false; auto Loaded = IsLoaded(); if (Loaded) { LgiTrace("%s:%i - PNG: %s\n", _FL, GetFullPath().Get()); } else { #if defined(WINDOWS) && defined(_DEBUG) auto ReleaseLib = LString(sLibrary)(0, -2); if (!Load(ReleaseLib)) #endif LgiTrace("%s:%i - Failed to load '%s'.\n", _FL, sLibrary); } } } DynFunc4( png_structp, png_create_read_struct, png_const_charp, user_png_ver, png_voidp, error_ptr, png_error_ptr, error_fn, png_error_ptr, warn_fn); DynFunc4( png_structp, png_create_write_struct, png_const_charp, user_png_ver, png_voidp, error_ptr, png_error_ptr, error_fn, png_error_ptr, warn_fn); DynFunc1( png_infop, png_create_info_struct, png_structp, png_ptr); DynFunc2( int, png_destroy_info_struct, png_structp, png_ptr, png_infopp, info_ptr); DynFunc3( int, png_destroy_read_struct, png_structpp, png_ptr_ptr, png_infopp, info_ptr_ptr, png_infopp, end_info_ptr_ptr); DynFunc2( int, png_destroy_write_struct, png_structpp, png_ptr_ptr, png_infopp, info_ptr_ptr); DynFunc3( int, png_set_read_fn, png_structp, png_ptr, png_voidp, io_ptr, png_rw_ptr, read_data_fn); DynFunc4( int, png_set_write_fn, png_structp, png_ptr, png_voidp, io_ptr, png_rw_ptr, write_data_fn, png_flush_ptr, output_flush_fn); DynFunc4( int, png_read_png, png_structp, png_ptr, png_infop, info_ptr, int, transforms, png_voidp, params); DynFunc4( int, png_write_png, png_structp, png_ptr, png_infop, info_ptr, int, transforms, png_voidp, params); DynFunc2( png_bytepp, png_get_rows, png_structp, png_ptr, png_infop, info_ptr); DynFunc3( int, png_set_rows, png_structp, png_ptr, png_infop, info_ptr, png_bytepp, row_pointers); DynFunc6( png_uint_32, png_get_iCCP, png_structp, png_ptr, png_const_infop, info_ptr, png_charpp, name, int*, compression_type, png_bytepp, profile, png_uint_32*, proflen); DynFunc6( int, png_set_iCCP, png_structp, png_ptr, png_infop, info_ptr, png_charp, name, int, compression_type, png_const_bytep, profile, png_uint_32, proflen); DynFunc5( png_uint_32, png_get_tRNS, png_structp, png_ptr, png_infop, info_ptr, png_bytep*, trans_alpha, int*, num_trans, png_color_16p*, trans_color); DynFunc3( png_uint_32, png_get_valid, png_structp, png_ptr, png_infop, info_ptr, png_uint_32, flag); DynFunc4( png_uint_32, png_get_PLTE, png_structp, png_ptr, png_infop, info_ptr, png_colorp*, palette, int*, num_palette); DynFunc2( png_uint_32, png_get_image_width, png_structp, png_ptr, png_infop, info_ptr); DynFunc2( png_uint_32, png_get_image_height, png_structp, png_ptr, png_infop, info_ptr); DynFunc2( png_byte, png_get_channels, png_structp, png_ptr, png_infop, info_ptr); #if 1 // PNG_LIBPNG_VER <= 10250 DynFunc2( png_byte, png_get_color_type, png_structp, png_ptr, png_infop, info_ptr); #else DynFunc2( png_byte, png_get_color_type, png_const_structp, png_ptr, png_const_infop, info_ptr); #endif DynFunc2( png_byte, png_get_bit_depth, png_structp, png_ptr, png_infop, info_ptr); DynFunc1( png_voidp, png_get_error_ptr, png_structp, png_ptr); DynFunc1( png_voidp, png_get_io_ptr, png_structp, png_ptr); DynFunc9( int, png_set_IHDR, png_structp, png_ptr, png_infop, info_ptr, png_uint_32, width, png_uint_32, height, int, bit_depth, int, color_type, int, interlace_method, int, compression_method, int, filter_method); DynFunc4( int, png_set_PLTE, png_structp, png_ptr, png_infop, info_ptr, png_colorp, palette, int, num_palette); DynFunc5( int, png_set_tRNS, png_structp, png_ptr, png_infop, info_ptr, png_bytep, trans_alpha, int, num_trans, png_color_16p, trans_color); /* DynFunc2( png_byte, png_get_interlace_type, png_const_structp, png_ptr, png_const_infop, info_ptr); */ }; class InitLibPng : public LMutex { LAutoPtr Png; public: LibPng *Get() { if (Lock(_FL)) { if (!Png) Png.Reset(new LibPng); Unlock(); } return Png; } InitLibPng() : LMutex("InitLibPng") { } } CurrentLibPng; #else #define LIBPNG #endif class GdcPng : public LFilter { static char PngSig[]; friend void PNGAPI LibPngError(png_structp Png, png_const_charp Msg); friend void PNGAPI LibPngWarning(png_structp Png, png_const_charp Msg); #if LIBPNG_SHARED LibPng *Lib; #endif int Pos; uchar *PrevScanLine; LSurface *pDC; LMemQueue DataPipe; LView *Parent; jmp_buf Here; public: GdcPng ( #if LIBPNG_SHARED LibPng *lib #endif ); ~GdcPng(); const char *GetComponentName() override { return "libpng"; } Format GetFormat() override { return FmtPng; } void SetMeter(int i) { if (Meter) Meter->Value(i); } int GetCapabilites() override { return FILTER_CAP_READ | FILTER_CAP_WRITE; } IoStatus ReadImage(LSurface *pDC, LStream *In) override; IoStatus WriteImage(LStream *Out, LSurface *pDC) override; bool GetVariant(const char *n, LVariant &v, const char *a = NULL) override { if (!_stricmp(n, LGI_FILTER_TYPE)) { v = "Png"; // Portable Network Graphic } else if (!_stricmp(n, LGI_FILTER_EXTENSIONS)) { v = "PNG"; } else return false; return true; } }; // Object Factory class GdcPngFactory : public LFilterFactory { bool CheckFile(const char *File, int Access, const uchar *Hint) { if (Hint) { return Hint[1] == 'P' && Hint[2] == 'N' && Hint[3] == 'G'; } else { return (File) ? stristr(File, ".png") != 0 : false; } } LFilter *NewObject() { return new GdcPng ( #if LIBPNG_SHARED CurrentLibPng.Get() #endif ); } } PngFactory; // Class impl char GdcPng::PngSig[] = { (char)137, 'P', 'N', 'G', '\r', '\n', (char)26, '\n', 0 }; GdcPng::GdcPng( #if LIBPNG_SHARED LibPng *lib #endif ) { #if LIBPNG_SHARED Lib = lib; #endif Parent = 0; Pos = 0; PrevScanLine = 0; } GdcPng::~GdcPng() { DeleteArray(PrevScanLine); } void PNGAPI LibPngError(png_structp Png, png_const_charp Msg) { GdcPng *This = (GdcPng*) #if LIBPNG_SHARED CurrentLibPng.Get()-> #endif png_get_error_ptr(Png); if (This) { printf("Libpng Error Message='%s'\n", Msg); if (This->Props) { LVariant v; This->Props->SetValue(LGI_FILTER_ERROR, v = (char*)Msg); } longjmp(This->Here, -1); } } void PNGAPI LibPngWarning(png_structp Png, png_const_charp Msg) { LgiTrace("LibPng Warning: %s\n", Msg); } void PNGAPI LibPngRead(png_structp Png, png_bytep Ptr, png_size_t Size) { LStream *s = (LStream*) #if LIBPNG_SHARED CurrentLibPng.Get()-> #endif png_get_io_ptr(Png); if (s) { s->Read(Ptr, Size); } else { LgiTrace("%s:%i - No this ptr? (%p)\n", __FILE__, __LINE__, Ptr); LAssert(0); } } struct PngWriteInfo { LStream *s; Progress *m; }; void PNGAPI LibPngWrite(png_structp Png, png_bytep Ptr, png_size_t Size) { PngWriteInfo *i = (PngWriteInfo*) #if LIBPNG_SHARED CurrentLibPng.Get()-> #endif png_get_io_ptr(Png); if (i) { i->s->Write(Ptr, Size); /* if (i->m) i->m->Value(Png->flush_rows); */ } else { LgiTrace("%s:%i - No this ptr?\n", __FILE__, __LINE__); LAssert(0); } } template void Read32_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 3; o->g = i->g >> 2; o->b = i->b >> 3; o++; i++; } } template void Read64_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 11; o->g = i->g >> 10; o->b = i->b >> 11; o++; i++; } } template void Read32_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o++; i++; } } template void Read64_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o++; i++; } } template void Read32_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o->a = 255; o++; i++; } } template void Read64_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o->a = 255; o++; i++; } } template void ReadAlpha32_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 3; o->g = i->g >> 2; o->b = i->b >> 3; o++; i++; } } template void ReadAlpha64_16(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 11; o->g = i->g >> 10; o->b = i->b >> 11; o++; i++; } } template void ReadAlpha32_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o++; i++; } } template void ReadAlpha64_24(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o++; i++; } } template void ReadAlpha32_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r; o->g = i->g; o->b = i->b; o->a = i->a; o++; i++; } } template void ReadAlpha64_32(Out *o, In *i, int Len) { In *e = i + Len; while (i < e) { o->r = i->r >> 8; o->g = i->g >> 8; o->b = i->b >> 8; o->a = i->a >> 8; o++; i++; } } LFilter::IoStatus GdcPng::ReadImage(LSurface *pDeviceContext, LStream *In) { LFilter::IoStatus Status = IoError; Pos = 0; pDC = pDeviceContext; DeleteArray(PrevScanLine); if (!pDC) { LAssert(!"No DC."); return Status; } LVariant v; if (Props && Props->GetValue(LGI_FILTER_PARENT_WND, v) && v.Type == GV_GVIEW) { Parent = (LView*)v.Value.Ptr; } #if LIBPNG_SHARED if (!Lib->IsLoaded() && !Lib->Load(sLibrary)) { LString s; s.Printf("libpng is missing (%s.%s)", sLibrary, LGI_LIBRARY_EXT); if (Props) Props->SetValue(LGI_FILTER_ERROR, v = s); else LgiTrace("%s:%i - %s\n", _FL, s.Get()); static bool Warn = true; if (Warn) { LgiTrace("%s:%i - Unable to load libpng (%s.%s).\n", _FL, sLibrary, LGI_LIBRARY_EXT); Warn = false; } return LFilter::IoComponentMissing; } #endif png_structp png_ptr = NULL; if (setjmp(Here)) { return Status; } png_ptr = LIBPNG png_create_read_struct(PNG_LIBPNG_VER_STRING, (void*)this, LibPngError, LibPngWarning); if (!png_ptr) { if (Props) Props->SetValue(LGI_FILTER_ERROR, v = "png_create_read_struct failed."); } else { png_infop info_ptr = LIBPNG png_create_info_struct(png_ptr); if (info_ptr) { LIBPNG png_set_read_fn(png_ptr, In, LibPngRead); #if 0 // What was this for again? int off = (char*)&png_ptr->io_ptr - (char*)png_ptr; if (!png_ptr->io_ptr) { printf("io_ptr offset = %i\n", off); LAssert(0); CurrentLibPng = 0; return false; } #endif LIBPNG png_read_png(png_ptr, info_ptr, 0, 0); png_bytepp Scan0 = LIBPNG png_get_rows(png_ptr, info_ptr); if (Scan0) { int BitDepth = LIBPNG png_get_bit_depth(png_ptr, info_ptr); int FinalBits = BitDepth == 16 ? 8 : BitDepth; int ColourType = LIBPNG png_get_color_type(png_ptr, info_ptr); int Channels = LIBPNG png_get_channels(png_ptr, info_ptr); int RequestBits = FinalBits * Channels; LColourSpace InCs = ColourType == PNG_COLOR_TYPE_GRAY_ALPHA ? CsIndex8 : LBitsToColourSpace(MAX(RequestBits, 8)); if (!pDC->Create( LIBPNG png_get_image_width(png_ptr, info_ptr), LIBPNG png_get_image_height(png_ptr, info_ptr), InCs, LSurface::SurfaceRequireExactCs)) { printf("%s:%i - LMemDC::Create(%i, %i, %i) failed.\n", _FL, LIBPNG png_get_image_width(png_ptr, info_ptr), LIBPNG png_get_image_height(png_ptr, info_ptr), RequestBits); } else { bool Error = false; #if 1 if (ColourType == PNG_COLOR_TYPE_GRAY_ALPHA) { pDC->HasAlpha(true); // Setup alpha channel } /* printf("PngRead %s->%s\n", LColourSpaceToString(InCs), LColourSpaceToString(pDC->GetColourSpace())); */ #endif // Copy in the scanlines int ActualBits = pDC->GetBits(); int ScanLen = LIBPNG png_get_image_width(png_ptr, info_ptr) * ActualBits / 8; LColourSpace OutCs = pDC->GetColourSpace(); for (int y=0; yY() && !Error; y++) { uchar *Scan = (*pDC)[y]; LAssert(Scan != NULL); switch (RequestBits) { case 1: { uchar *o = Scan; uchar *e = Scan + pDC->X(); uchar *i = Scan0[y]; uchar Mask = 0x80; while (o < e) { *o++ = (*i & Mask) ? 1 : 0; Mask >>= 1; if (!Mask) { i++; Mask = 0x80; } } break; } case 2: { uchar *i = Scan0[y]; uchar *o = Scan; for (int x=0; xX(); x++) { switch (x & 3) { case 0: *o++ = (*i >> 6) & 0x3; break; case 1: *o++ = (*i >> 4) & 0x3; break; case 2: *o++ = (*i >> 2) & 0x3; break; case 3: *o++ = (*i++ >> 0) & 0x3; break; } } break; } case 4: { uchar *i = Scan0[y]; uchar *o = Scan; for (int x=0; xX(); x++) { if (x & 1) *o++ = *i++ & 0xf; else *o++ = (*i >> 4) & 0xf; } break; } case 8: { memcpy(Scan, Scan0[y], ScanLen); break; } case 16: { if (ColourType == PNG_COLOR_TYPE_GRAY_ALPHA) { uint8_t *grey = Scan; uint8_t *alpha = (*(pDC->AlphaDC()))[y]; LAssert(grey && alpha); uint8_t *end = grey + pDC->X(); uint8_t *in = Scan0[y]; while (grey < end) { *grey++ = *in++; *alpha++ = *in++; } } else { memcpy(Scan, Scan0[y], ScanLen); } break; } case 24: { switch (OutCs) { #define Read24Case(name, bits) \ case Cs##name: \ { \ if (LIBPNG png_get_bit_depth(png_ptr, info_ptr) == 16) \ Read64_##bits((L##name*)Scan, (Png48*)Scan0[y], pDC->X()); \ else \ Read32_##bits((L##name*)Scan, (Png24*)Scan0[y], pDC->X()); \ break; \ } Read24Case(Rgb16, 16); Read24Case(Bgr16, 16); Read24Case(Rgb24, 24); Read24Case(Bgr24, 24); Read24Case(Xrgb32, 24); Read24Case(Rgbx32, 24); Read24Case(Xbgr32, 24); Read24Case(Bgrx32, 24); Read24Case(Rgba32, 32); Read24Case(Bgra32, 32); Read24Case(Argb32, 32); Read24Case(Abgr32, 32); default: LgiTrace("%s:%i - Unsupported colour space: 0x%x (%s)\n", _FL, pDC->GetColourSpace(), LColourSpaceToString(pDC->GetColourSpace())); LAssert(!"Not impl."); break; } break; } case 32: { switch (pDC->GetColourSpace()) { #define Read32Case(name, bits) \ case Cs##name: \ { \ if (LIBPNG png_get_bit_depth(png_ptr, info_ptr) == 16) \ ReadAlpha64_##bits((L##name*)Scan, (Png64*)Scan0[y], pDC->X()); \ else \ ReadAlpha32_##bits((L##name*)Scan, (Png32*)Scan0[y], pDC->X()); \ break; \ } Read32Case(Rgb16, 16); Read32Case(Bgr16, 16); Read32Case(Rgb24, 24); Read32Case(Bgr24, 24); Read32Case(Xrgb32, 24); Read32Case(Rgbx32, 24); Read32Case(Xbgr32, 24); Read32Case(Bgrx32, 24); Read32Case(Rgba32, 32); Read32Case(Bgra32, 32); Read32Case(Argb32, 32); Read32Case(Abgr32, 32); default: LgiTrace("%s:%i - Unsupported colour space: 0x%x (%s)\n", _FL, pDC->GetColourSpace(), LColourSpaceToString(pDC->GetColourSpace())); LAssert(!"Not impl."); if (Props) Props->SetValue(LGI_FILTER_ERROR, v = "Missing scan convertor"); Error = true; break; } break; } default: { if (ActualBits == RequestBits) { memcpy(Scan, Scan0[y], ScanLen); } else { LAssert(!"Yeah you need to impl a convertor here."); if (Props) Props->SetValue(LGI_FILTER_ERROR, v = "Missing scan convertor"); Error = true; } break; } } } if (RequestBits == 32) { // bool IsPreMul = pDC->IsPreMultipliedAlpha(); pDC->ConvertPreMulAlpha(true); } if (ActualBits <= 8) { // Copy in the palette png_colorp pal; int num_pal = 0; if (LIBPNG png_get_PLTE(png_ptr, info_ptr, &pal, &num_pal) == PNG_INFO_PLTE) { LPalette *Pal = new LPalette(0, num_pal); if (Pal) { for (int i=0; ir = pal[i].red; Rgb->g = pal[i].green; Rgb->b = pal[i].blue; } } pDC->Palette(Pal, true); } } if (LIBPNG png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_bytep trans_alpha = NULL; png_color_16p trans_color; int num_trans; if (LIBPNG png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color)) { pDC->HasAlpha(true); LSurface *Alpha = pDC->AlphaDC(); if (Alpha) { if (trans_alpha) { for (int y=0; yY(); y++) { uchar *a = (*Alpha)[y]; uchar *p = (*pDC)[y]; for (int x=0; xX(); x++) { if (p[x] < num_trans) { a[x] = trans_alpha[p[x]]; } else { a[x] = 0xff; } } } } else if (trans_color) { for (int y=0; yY(); y++) { uchar *a = (*Alpha)[y]; uchar *p = (*pDC)[y]; for (int x=0; xX(); x++) { a[x] = p[x] == trans_color->index ? 0x00 : 0xff; } } } } else { printf("%s:%i - No alpha channel.\n", _FL); } } else { printf("%s:%i - Bad trans ptr.\n", _FL); } } } Status = Error ? IoError : IoSuccess; } } else LgiTrace("%s:%i - png_get_rows failed.\n", _FL); LIBPNG png_destroy_info_struct(png_ptr, &info_ptr); } png_charp ProfName = 0; int CompressionType = 0; png_bytep ColProf = 0; png_uint_32 ColProfLen = 0; if (LIBPNG png_get_iCCP(png_ptr, info_ptr, &ProfName, &CompressionType, &ColProf, &ColProfLen) && Props) { v.SetBinary(ColProfLen, ColProf); Props->SetValue(LGI_FILTER_COLOUR_PROF, v); } LIBPNG png_destroy_read_struct(&png_ptr, 0, 0); } return Status; } LFilter::IoStatus GdcPng::WriteImage(LStream *Out, LSurface *pDC) { LFilter::IoStatus Status = IoError; LVariant Transparent; bool HasTransparency = false; COLOUR Back = 0; LVariant v; if (!pDC) return LFilter::IoError; #if LIBPNG_SHARED if (!Lib->IsLoaded() && !Lib->Load(sLibrary)) { static bool Warn = true; if (Warn) { LgiTrace("%s:%i - Unabled to load libpng.\n", _FL); Warn = false; } return LFilter::IoComponentMissing; } #endif // Work out whether the image has transparency if (pDC->GetBits() == 32) { // Check alpha channel for (int y=0; yY() && !HasTransparency; y++) { System32BitPixel *p = (System32BitPixel*)(*pDC)[y]; if (!p) break; System32BitPixel *e = p + pDC->X(); while (p < e) { if (p->a < 255) { HasTransparency = true; break; } p++; } } } else if (pDC->AlphaDC()) { LSurface *a = pDC->AlphaDC(); if (a) { for (int y=0; yY() && !HasTransparency; y++) { uint8_t *p = (*a)[y]; if (!p) break; uint8_t *e = p + a->X(); while (p < e) { if (*p < 255) { HasTransparency = true; break; } p++; } } } } if (Props) { if (Props->GetValue(LGI_FILTER_PARENT_WND, v) && v.Type == GV_GVIEW) { Parent = (LView*)v.Value.Ptr; } if (Props->GetValue(LGI_FILTER_BACKGROUND, v)) { Back = v.CastInt32(); } Props->GetValue(LGI_FILTER_TRANSPARENT, Transparent); } #ifdef FILTER_UI if (Parent && Transparent.IsNull()) { LAssert(!"Move this to the parent app."); // put up a dialog to ask about transparent colour auto Dlg = new LTransparentDlg(Parent, &Transparent); Dlg->DoModal([this, Dlg](auto dlg, auto code) { LVariant v; if (!code && Props) Props->SetValue("Cancel", v = 1); delete Dlg; return IoCancel; }); } #endif if (setjmp(Here) == 0 && pDC && Out) { LVariant ColProfile; if (Props) { Props->GetValue(LGI_FILTER_COLOUR_PROF, ColProfile); } // setup png_structp png_ptr = LIBPNG png_create_write_struct( PNG_LIBPNG_VER_STRING, (void*)this, LibPngError, LibPngWarning); if (png_ptr) { png_infop info_ptr = LIBPNG png_create_info_struct(png_ptr); if (info_ptr) { Out->SetSize(0); PngWriteInfo WriteInfo; WriteInfo.s = Out; WriteInfo.m = Meter; LIBPNG png_set_write_fn(png_ptr, &WriteInfo, LibPngWrite, 0); // png_set_write_status_fn(png_ptr, write_row_callback); bool KeyAlpha = false; bool ChannelAlpha = false; LMemDC *pTemp = 0; if (pDC->AlphaDC() && HasTransparency) { pTemp = new LMemDC(pDC->X(), pDC->Y(), System32BitColourSpace); if (pTemp) { pTemp->Colour(0); pTemp->Rectangle(); pTemp->Op(GDC_ALPHA); pTemp->Blt(0, 0, pDC); pTemp->Op(GDC_SET); pDC = pTemp; ChannelAlpha = true; } } else { if (Transparent.CastInt32() && Props && Props->GetValue(LGI_FILTER_BACKGROUND, v)) { KeyAlpha = true; } } int Ar = R32(Back); int Ag = G32(Back); int Ab = B32(Back); if (pDC->GetBits() == 32) { if (!ChannelAlpha && !KeyAlpha) { for (int y=0; yY(); y++) { System32BitPixel *s = (System32BitPixel*) (*pDC)[y]; for (int x=0; xX(); x++) { if (s[x].a < 0xff) { ChannelAlpha = true; y = pDC->Y(); break; } } } } } bool ExtraAlphaChannel = ChannelAlpha || (pDC->GetBits() > 8 ? KeyAlpha : 0); int ColourType; if (pDC->GetBits() <= 8) { if (pDC->Palette()) ColourType = PNG_COLOR_TYPE_PALETTE; else ColourType = PNG_COLOR_TYPE_GRAY; } else if (ExtraAlphaChannel) { ColourType = PNG_COLOR_TYPE_RGB_ALPHA; } else { ColourType = PNG_COLOR_TYPE_RGB; } LIBPNG png_set_IHDR(png_ptr, info_ptr, pDC->X(), pDC->Y(), 8, ColourType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (ColProfile.Type == GV_BINARY) { LIBPNG png_set_iCCP(png_ptr, info_ptr, (png_charp)"ColourProfile", 0, (png_const_bytep) ColProfile.Value.Binary.Data, (png_uint_32) ColProfile.Value.Binary.Length); } int TempLine = pDC->X() * ((pDC->GetBits() <= 8 ? 1 : 3) + (ExtraAlphaChannel ? 1 : 0)); uchar *TempBits = new uchar[pDC->Y() * TempLine]; if (Meter) Meter->SetRange(pDC->Y()); switch (pDC->GetBits()) { case 8: { // Output the palette LPalette *Pal = pDC->Palette(); if (Pal) { int Colours = Pal->GetSize(); LAutoPtr PngPal(new png_color[Colours]); if (PngPal) { for (int i=0; ir; PngPal[i].green = Rgb->g; PngPal[i].blue = Rgb->b; } } LIBPNG png_set_PLTE(png_ptr, info_ptr, PngPal, Colours); } } // Copy the pixels for (int y=0; yY(); y++) { uchar *s = (*pDC)[y]; Png8 *d = (Png8*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { *d++ = *s++; } } // Setup the transparent palette entry if (KeyAlpha) { static png_byte Trans[256]; for (uint n=0; nbit_depth = 8; //info_ptr->channels = 3 + (ExtraAlphaChannel ? 1 : 0); //info_ptr->color_type = PNG_COLOR_TYPE_RGB | (KeyAlpha ? PNG_COLOR_MASK_ALPHA : 0); for (int y=0; yY(); y++) { uint16 *s = (uint16*) (*pDC)[y]; if (pDC->GetBits() == 15) { if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc15(*s); d->g = Gc15(*s); d->b = Bc15(*s); d->a = (d->r == Ar && d->g == Ag && d->b == Ab) ? 0 : 0xff; s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc15(*s); d->g = Gc15(*s); d->b = Bc15(*s); s++; d++; } } } else { if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc16(*s); d->g = Gc16(*s); d->b = Bc16(*s); d->a = (d->r == Ar && d->g == Ag && d->b == Ab) ? 0 : 0xff; s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = Rc16(*s); d->g = Gc16(*s); d->b = Bc16(*s); s++; d++; } } } } break; } case 24: { //info_ptr->bit_depth = 8; //info_ptr->channels = 3 + (KeyAlpha ? 1 : 0); //info_ptr->color_type = PNG_COLOR_TYPE_RGB | (KeyAlpha ? PNG_COLOR_MASK_ALPHA : 0); for (int y=0; yY(); y++) { System24BitPixel *s = (System24BitPixel*) (*pDC)[y]; if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = (s->r == Ar && s->g == Ag && s->b == Ab) ? 0 : 0xff; s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } } break; } case 32: { //info_ptr->bit_depth = 8; //info_ptr->channels = 3 + (ExtraAlphaChannel ? 1 : 0); //info_ptr->color_type = PNG_COLOR_TYPE_RGB | (ExtraAlphaChannel ? PNG_COLOR_MASK_ALPHA : 0); for (int y=0; yY(); y++) { System32BitPixel *s = (System32BitPixel*) (*pDC)[y]; if (ChannelAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; s++; d++; } } else if (KeyAlpha) { Png32 *d = (Png32*) (TempBits + (TempLine * y)); Png32 *e = d + pDC->X(); while (d < e) { if (s->a == 0 || (s->r == Ar && s->g == Ag && s->b == Ab) ) { d->r = 0; d->g = 0; d->b = 0; d->a = 0; } else { d->r = s->r; d->g = s->g; d->b = s->b; d->a = s->a; } s++; d++; } } else { Png24 *d = (Png24*) (TempBits + (TempLine * y)); for (int x=0; xX(); x++) { d->r = s->r; d->g = s->g; d->b = s->b; s++; d++; } } } break; } default: { goto CleanUp; } } LArray row; if (row.Length(pDC->Y())) { for (int y=0; yY(); y++) { row[y] = TempBits + (TempLine * y); } LIBPNG png_set_rows(png_ptr, info_ptr, row.AddressOf()); LIBPNG png_write_png(png_ptr, info_ptr, 0, 0); Status = IoSuccess; } DeleteArray(TempBits); DeleteObj(pTemp); LIBPNG png_destroy_info_struct(png_ptr, &info_ptr); } CleanUp: LIBPNG png_destroy_write_struct(&png_ptr, NULL); } } return Status; }