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,1548 +1,1546 @@ /*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.h" #include "GPalette.h" #ifdef __CYGWIN__ #include "png.h" #endif #include #include #include #include "GLibraryUtils.h" #ifdef FILTER_UI #include "GTransparentDlg.h" #endif #include "GVariant.h" // Pixel formats typedef uint8_t Png8; typedef GRgb24 Png24; typedef GRgba32 Png32; typedef GRgb48 Png48; typedef GRgba64 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 #if LIBPNG_SHARED #define LIBPNG Lib-> const char sLibrary[] = #if defined(MAC) "libpng15.15.4.0" #else #if defined(__CYGWIN__) "cygpng12" #else "libpng" #ifdef _MSC_VER _MSC_VER_STR #if defined(WIN64) "x64" #else "x32" #endif #endif #endif #endif ; // Library interface class LibPng : public GLibrary { public: LibPng() : GLibrary(sLibrary) { if (!IsLoaded()) { #if defined(MAC) if (!Load("/opt/local/lib/libpng.dylib")) #elif defined(LINUX) if (!Load("libpng16")) #endif { static bool First = true; if (First) { LgiTrace("%s:%i - Failed to load libpng.\n", _FL); First = false; } } } else { #if 0 char File[256]; GetModuleFileName(Handle(), File, sizeof(File)); LgiTrace("%s:%i - PNG: %s\n", _FL, File); #endif } } 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 { GAutoPtr Png; public: LibPng *Get() { if (Lock(_FL)) { if (!Png) Png.Reset(new LibPng); Unlock(); } return Png; } } CurrentLibPng; #else #define LIBPNG #endif class GdcPng : public GFilter { 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; GSurface *pDC; GMemQueue DataPipe; GView *Parent; jmp_buf Here; public: GdcPng ( #if LIBPNG_SHARED LibPng *lib #endif ); ~GdcPng(); const char *GetComponentName() { return "libpng"; } Format GetFormat() { return FmtPng; } void SetMeter(int i) { if (Meter) Meter->Value(i); } int GetCapabilites() { return FILTER_CAP_READ | FILTER_CAP_WRITE; } IoStatus ReadImage(GSurface *pDC, GStream *In); IoStatus WriteImage(GStream *Out, GSurface *pDC); bool GetVariant(const char *n, GVariant &v, char *a) { 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 GFilterFactory { 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; } } GFilter *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) { GVariant 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) { GStream *s = (GStream*) #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); LgiAssert(0); } } struct PngWriteInfo { GStream *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__); LgiAssert(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++; } } GFilter::IoStatus GdcPng::ReadImage(GSurface *pDeviceContext, GStream *In) { GFilter::IoStatus Status = IoError; Pos = 0; pDC = pDeviceContext; DeleteArray(PrevScanLine); GVariant v; if (Props && Props->GetValue(LGI_FILTER_PARENT_WND, v) && v.Type == GV_GVIEW) { Parent = (GView*)v.Value.Ptr; } #if LIBPNG_SHARED if (!Lib->IsLoaded() && !Lib->Load(sLibrary)) { GString 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 GFilter::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); LgiAssert(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; GColourSpace InCs = ColourType == PNG_COLOR_TYPE_GRAY_ALPHA ? CsIndex8 : GBitsToColourSpace(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, GSurface::SurfaceRequireExactCs)) { printf("%s:%i - GMemDC::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", GColourSpaceToString(InCs), GColourSpaceToString(pDC->GetColourSpace())); */ #endif // Copy in the scanlines int ActualBits = pDC->GetBits(); int ScanLen = LIBPNG png_get_image_width(png_ptr, info_ptr) * ActualBits / 8; GColourSpace OutCs = pDC->GetColourSpace(); for (int y=0; yY() && !Error; y++) { uchar *Scan = (*pDC)[y]; LgiAssert(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 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]; LgiAssert(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((G##name*)Scan, (Png48*)Scan0[y], pDC->X()); \ else \ Read32_##bits((G##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(), GColourSpaceToString(pDC->GetColourSpace())); LgiAssert(!"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((G##name*)Scan, (Png64*)Scan0[y], pDC->X()); \ else \ ReadAlpha32_##bits((G##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(), GColourSpaceToString(pDC->GetColourSpace())); LgiAssert(!"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 { LgiAssert(!"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) { GPalette *Pal = new GPalette(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); GSurface *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; } GFilter::IoStatus GdcPng::WriteImage(GStream *Out, GSurface *pDC) { GFilter::IoStatus Status = IoError; GVariant Transparent; bool HasTransparency = false; COLOUR Back = 0; GVariant v; if (!pDC) return GFilter::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 GFilter::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()) { GSurface *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 = (GView*)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()) { // put up a dialog to ask about transparent colour GTransparentDlg Dlg(Parent, &Transparent); if (!Dlg.DoModal()) { if (Props) Props->SetValue("Cancel", v = 1); return IoCancel; } } #endif if (setjmp(Here) == 0 && pDC && Out) { GVariant 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; GMemDC *pTemp = 0; if (pDC->AlphaDC() && HasTransparency) { pTemp = new GMemDC(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", NULL, (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->SetLimits(0, pDC->Y()); switch (pDC->GetBits()) { case 8: { // Output the palette GPalette *Pal = pDC->Palette(); if (Pal) { int Colours = Pal->GetSize(); GAutoPtr 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; } } - png_bytep *row = new png_bytep[pDC->Y()]; - if (row) + GArray 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); + LIBPNG png_set_rows(png_ptr, info_ptr, row.AddressOf()); LIBPNG png_write_png(png_ptr, info_ptr, 0, 0); Status = IoSuccess; - - DeleteArray(row); } DeleteArray(TempBits); DeleteObj(pTemp); LIBPNG png_destroy_info_struct(png_ptr, &info_ptr); } CleanUp: LIBPNG png_destroy_write_struct(&png_ptr, NULL); } } return Status; } diff --git a/src/common/Lgi/GSubProcess.cpp b/src/common/Lgi/GSubProcess.cpp --- a/src/common/Lgi/GSubProcess.cpp +++ b/src/common/Lgi/GSubProcess.cpp @@ -1,1023 +1,1024 @@ /** \file \brief Sub-process wrapper. This class runs one or more sub-processes chained together by pipes. Example: GSubProcess p1("ls", "-l"); GSubProcess p2("grep", "string"); p1.Connect(&p2); p1.Start(true, false); int r; char Buf[256]; while ((r = p1.Read(Buf, sizeof(Buf))) > 0) { // So something with 'Buf' } */ #if defined(MAC) || defined(POSIX) #define _GNU_SOURCE #include #include #include #include #endif #ifdef BEOS #include #endif #include "Lgi.h" #include "GSubProcess.h" #include "GToken.h" #define DEBUG_SUBPROCESS 0 #if defined(WIN32) #define NULL_PIPE NULL #define ClosePipe CloseHandle #else #define NULL_PIPE -1 #define ClosePipe close #define INVALID_PID -1 #endif GSubProcess::Pipe::Pipe() { Read = Write = NULL_PIPE; } bool GSubProcess::Pipe::Create ( #ifdef WIN32 LPSECURITY_ATTRIBUTES pAttr #else void *UnusedParam #endif ) { #if defined(WIN32) return CreatePipe(&Read, &Write, pAttr, 0) != 0; #else return pipe(Handles) != NULL_PIPE; #endif } void GSubProcess::Pipe::Close() { if (Read != NULL_PIPE) { ClosePipe(Read); Read = NULL_PIPE; } if (Write != NULL_PIPE) { ClosePipe(Write); Write = NULL_PIPE; } } GSubProcess::GSubProcess(const char *exe, const char *args) { #if defined(POSIX) ChildPid = INVALID_PID; ExitValue = -1; #elif defined(WIN32) ChildPid = NULL; ChildHnd = NULL; ExitValue = 0; #endif NewGroup = true; ErrorCode = 0; Parent = Child = NULL; Exe = exe; Args.Add(Exe); EnvironmentChanged = false; ExternIn = NULL_PIPE; ExternOut = NULL_PIPE; #if DEBUG_SUBPROCESS LgiTrace("%s:%i - %p::GSubProcess('%s','%s')\n", _FL, this, exe, args); #endif char *s; while ((s = LgiTokStr(args))) { Args.Add(s); } } GSubProcess::~GSubProcess() { #if defined(POSIX) Io.Close(); #endif if (Child) { LgiAssert(Child->Parent == this); Child->Parent = NULL; } if (Parent) { LgiAssert(Parent->Child == this); Parent->Child = NULL; } } #ifndef WINDOWS extern char **environ; #endif GSubProcess::Variable *GSubProcess::GetEnvVar(const char *Var, bool Create) { if (Environment.Length() == 0) { // Read all variables in #ifdef WINDOWS LPWCH e = GetEnvironmentStringsW(); if (e) { char16 *s = e; while (*s) { char16 *eq = StrchrW(s, '='); if (!eq) break; ptrdiff_t NameChars = eq - s; if (NameChars > 0) { Variable &v = Environment.New(); v.Var.SetW(s, eq - s); eq++; v.Val.SetW(eq); } eq += StrlenW(eq); s = eq + 1; } FreeEnvironmentStringsW(e); } #else for (int i=0; environ[i]; i++) { auto p = GString(environ[i]).Split("=", 1); if (p.Length() == 2) { Variable &v = Environment.New(); v.Var = p[0]; v.Val = p[1]; } } #endif } for (unsigned i=0; iVal.Get() : NULL; } bool GSubProcess::SetEnvironment(const char *Var, const char *Value) { Variable *v = GetEnvVar(Var, true); if (!v) return false; bool IsPath = !_stricmp(Var, "PATH"); GStringPipe a; const char *s = Value; while (*s) { char *n = strchr(s, '%'); char *e = n ? strchr(n + 1, '%') : NULL; if (n && e) { a.Write(s, (int) (n-s)); n++; ptrdiff_t bytes = e - n; char Name[128]; if (bytes > sizeof(Name) - 1) bytes = sizeof(Name)-1; memcpy(Name, n, bytes); Name[bytes] = 0; const char *existing = GetEnvironment(Name); if (existing) { a.Write(existing, (int)strlen(existing)); } s = e + 1; } else { a.Write(s, (int)strlen(s)); break; } } v->Val = a.NewGStr(); if (IsPath) { // Remove missing paths from the list GToken t(v->Val, LGI_PATH_SEPARATOR); GStringPipe p; for (unsigned i=0; iVal = p.NewGStr(); } EnvironmentChanged = true; return true; } bool GSubProcess::GetValue(const char *Var, ::GVariant &Value) { switch (LgiStringToDomProp(Var)) { case StreamReadable: { #ifdef WINNATIVE char Buf[32] = ""; DWORD lpBytesRead = 0; BOOL b = PeekNamedPipe( ChildOutput.Read, Buf, sizeof(Buf), &lpBytesRead, NULL, NULL); Value = b && lpBytesRead > 0; break; #endif } /* case StreamWritable: { break; } */ default: return false; } return true; } void GSubProcess::SetStdin(OsFile Hnd) { ExternIn = Hnd; } void GSubProcess::SetStdout(OsFile Hnd) { ExternOut = Hnd; } void GSubProcess::Connect(GSubProcess *child) { Child = child; if (Child) { Child->Parent = this; } } bool GSubProcess::Start(bool ReadAccess, bool WriteAccess, bool MapStderrToStdout) { bool Status = false; #if USE_SIMPLE_FORK int in[2]; if (pipe(in) == -1) { printf("parent: Failed to create stdin pipe"); return false; } int out[2]; if (pipe(out) == -1) { printf("parent: Failed to create stdout pipe"); return false; } ChildPid = fork(); if (ChildPid == 0) { // We are in the child process. if (InitialFolder) { chdir(InitialFolder); } // Child shouldn't write to its stdin. if (close(in[1])) printf("%s:%i - close failed.\n", _FL); // Child shouldn't read from its stdout. if (close(out[0])) printf("%s:%i - close failed.\n", _FL); // Redirect stdin and stdout for the child process. if (dup2(in[0], fileno(stdin)) == -1) { printf("%s:%i - child[pre-exec]: Failed to redirect stdin for child\n", _FL); return false; } if (close(in[0])) printf("%s:%i - close failed.\n", _FL); if (dup2(out[1], fileno(stdout)) == -1) { printf("%s:%i - child[pre-exec]: Failed to redirect stdout for child\n", _FL); return false; } if (dup2(out[1], fileno(stderr)) == -1) { printf("%s:%i - child[pre-exec]: Failed to redirect stderr for child\n", _FL); return false; } close(out[1]); // Execute the child Args.Add(NULL); if (Environment.Length()) { GString::Array Vars; GArray Env; Vars.SetFixedLength(false); for (auto v : Environment) { GString &s = Vars.New(); s.Printf("%s=%s", v.Var.Get(), v.Val.Get()); Env.Add(s.Get()); } Env.Add(NULL); execve(Exe, &Args[0], Env.AddressOf()); } else { execvp(Exe, &Args[0]); } // Execution will pass to here if the 'Exe' can't run or doesn't exist // So by exiting with an error the parent process can handle it. exit(GSUBPROCESS_ERROR); } else { // We are in the parent process. if (ChildPid == -1) { printf("%s:%i - parent: Failed to create child", _FL); return false; } // Parent shouldn't read from child's stdin. if (close(in[0])) printf("%s:%i - close failed.\n", _FL); // Parent shouldn't write to child's stdout. if (close(out[1])) printf("%s:%i - close failed.\n", _FL); Io.Read = out[0]; Io.Write = in[1]; // printf("USE_SIMPLE_FORK success.\n"); return true; } #else #if DEBUG_SUBPROCESS LgiTrace("%s:%i - %p::Start(%i,%i,%i)\n", _FL, this, ReadAccess, WriteAccess, MapStderrToStdout); #endif // Find the end of the process list ::GArray p; for (GSubProcess *s=this; s; s=s->Child) { LgiAssert(!s->Child || s->Child->Parent == s); p.Add(s); } size_t Kids = p.Length() + 1; #ifdef WIN32 SECURITY_ATTRIBUTES Attr; Attr.nLength = sizeof(SECURITY_ATTRIBUTES); Attr.bInheritHandle = true; Attr.lpSecurityDescriptor = NULL; #else int Attr = 0; #endif #if defined(POSIX) ::GArray Pipes; Pipes.Length(Kids); Pipes[0].Create(&Attr); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[%i].create %i,%i\n", _FL, 0, Pipes[0].Read, Pipes[0].Write); #endif Status = true; for (int i=1; iChildPid = fork(); if (sp->ChildPid == INVALID_PID) { LgiTrace("%s:%i - fork failed with %i", _FL, errno); exit(1); } else if (sp->ChildPid == 0) { if (InitialFolder) { chdir(InitialFolder); } // Close irrelevant pipes for (int j = 0; j < i-1; j++) { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* pipe[%i].close %i,%i\n", _FL, j, Pipes[j].Read, Pipes[j].Write); #endif Pipes[j].Close(); } // Set up STDIN and STDOUT Pipe &in = Pipes[i-1]; Pipe &out = Pipes[i]; #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* %i) Child init %i->'%s'->%i\n", _FL, i, in.Read, sp->Exe.Get(), out.Write); #endif #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Dupe %i->%i\n", _FL, in.Read, STDIN_FILENO); #endif Dupe(in.Read, STDIN_FILENO); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Close %i\n", _FL, in.Write); #endif close(in.Write); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Dupe %i->%i\n", _FL, out.Write, STDOUT_FILENO); #endif Dupe(out.Write, STDOUT_FILENO); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Dupe %i->%i\n", out.Write, STDERR_FILENO); #endif Dupe(out.Write, STDERR_FILENO); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *CHILD* Close %i\n", _FL, out.Read); #endif close(out.Read); fsync(STDOUT_FILENO); LgiSleep(100); // Execute the child sp->Args.Add(NULL); execvp(sp->Exe, &sp->Args[0]); LgiTrace("%s:%i - execvp('%s').\n", _FL, sp->Exe.Get()); for (int i=0; iArgs.Length(); i++) LgiTrace("%s:%i - Args[%i]='%s'\n", _FL, i, sp->Args[i]); Status = false; break; } } // Close irrelevant pipes for (int j = 1; j < Kids - 1; j++) { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[%i].close %i,%i\n", _FL, j, Pipes[j].Read, Pipes[j].Write); #endif Pipes[j].Close(); } #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[0].close %i, pipe[%i].close %i\n", _FL, Pipes[0].Read, Pipes.Length()-1, Pipes.Last().Write); #endif close(Pipes[0].Read); close(Pipes.Last().Write); // Set the input and output pipes for this sub-process. if (WriteAccess) Io.Write = Pipes[0].Write; else { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[0].close %i\n", _FL, Pipes[0].Write); #endif close(Pipes[0].Write); } if (ReadAccess) Io.Read = Pipes.Last().Read; else { #if DEBUG_SUBPROCESS LgiTrace("%s:%i - *PARENT* pipe[%i].close %i\n", _FL, Pipes.Length()-1, Pipes.Last().Read); #endif close(Pipes.Last().Read); } // LgiTrace("Final Handles %i, %i\n", Io.Read, Io.Write); #elif defined(WIN32) GAutoWString WExe; if (FileExists(Exe)) { WExe.Reset(Utf8ToWide(Exe)); } else { char *Ext = LgiGetExtension(Exe); bool HasExt = Ext && _stricmp(Ext, "exe") == 0; #if defined(WIN32) && !defined(PLATFORM_MINGW) GToken p; char *sPath = NULL; size_t sSize; errno_t err = _dupenv_s(&sPath, &sSize, "PATH"); if (err == 0) p.Parse(sPath, LGI_PATH_SEPARATOR); free(sPath); #else GToken p(getenv("PATH"), LGI_PATH_SEPARATOR); #endif for (unsigned i=0; i 0) { WArg[Ch++] = ' '; } if (strchr(a, ' ')) Ch += swprintf_s(WArg+Ch, CountOf(WArg)-Ch, L"\"%s\"", aw.Get()); else Ch += swprintf_s(WArg+Ch, CountOf(WArg)-Ch, L"%s", aw.Get()); } #if DEBUG_SUBPROCESS LgiTrace("%s:%i - Args='%S'\n", _FL, WArg); #endif bool HasExternIn = ExternIn != NULL_PIPE; #if DEBUG_SUBPROCESS LgiTrace("%s:%i - Oringinal handles, out=%p, in=%p, HasExternIn=%i\n", _FL, OldStdout, OldStdin, HasExternIn); #endif if (ChildOutput.Create(&Attr) && (HasExternIn || ChildInput.Create(&Attr))) { if (!SetHandleInformation(ChildOutput.Read, HANDLE_FLAG_INHERIT, 0)) LgiTrace("%s:%i - SetHandleInformation failed.\n", _FL); if (!HasExternIn && !SetHandleInformation(ChildInput.Write, HANDLE_FLAG_INHERIT, 0)) LgiTrace("%s:%i - SetHandleInformation failed.\n", _FL); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - Output Pipe: rd=%p, wr=%p\n", _FL, ChildOutput.Read, ChildOutput.Write); if (!HasExternIn) LgiTrace("%s:%i - Input Pipe: rd=%p, wr=%p\n", _FL, ChildInput.Read, ChildInput.Write); #endif STARTUPINFOW Info; ZeroObj(Info); Info.cb = sizeof(Info); PROCESS_INFORMATION ProcInfo; ZeroObj(ProcInfo); Info.dwFlags = STARTF_USESTDHANDLES; Info.hStdOutput = ChildOutput.Write; Info.hStdInput = HasExternIn ? ExternIn : ChildInput.Read; if (MapStderrToStdout) Info.hStdError = ChildOutput.Write; GAutoWString WInitialFolder(Utf8ToWide(InitialFolder)); #if DEBUG_SUBPROCESS LgiTrace("%s:%i - WInitialFolder=%S, EnvironmentChanged=%i\n", _FL, WInitialFolder.Get(), EnvironmentChanged); #endif GAutoWString WEnv; if (EnvironmentChanged) { GMemQueue q(256); for (unsigned i=0; i 0) p.Write(Buf, Rd); else break; } return p.NewGStr(); } ssize_t GSubProcess::Read(void *Buf, ssize_t Size, int TimeoutMs) { #if defined(POSIX) bool DoRead = true; if (TimeoutMs) { OsSocket s = Io.Read; if (ValidSocket(s)) { struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set r; FD_ZERO(&r); FD_SET(s, &r); int v = select((int)s+1, &r, 0, 0, &t); if (v > 0 && FD_ISSET(s, &r)) { DoRead = true; } else { // printf("SubProc not readable..\n"); return 0; } } else LgiTrace("%s:%i - Invalid socket.\n", _FL); } return (int)read(Io.Read, Buf, Size); #else DWORD Rd = -1, Sz; if (!ReadFile(ChildOutput.Read, Buf, AssertCast(Sz, Size), &Rd, NULL)) return -1; return Rd; #endif } int GSubProcess::Peek() { #if defined(POSIX) int bytesAvailable = 0; int r = ioctl(Io.Read, FIONREAD, &bytesAvailable); return r ? -1 : bytesAvailable; #else DWORD Rd = 0, Avail = 0; char Buf[32]; if (PeekNamedPipe(ChildOutput.Read, Buf, sizeof(Buf), &Rd, &Avail, NULL)) return Rd; return 0; #endif } bool GSubProcess::Write(GString s) { auto Wr = Write(s.Get(), s.Length()); return Wr == s.Length(); } ssize_t GSubProcess::Write(const void *Buf, ssize_t Size, int Flags) { #if defined(POSIX) return (int)write(Io.Write, Buf, Size); #else DWORD Wr = -1, Sz; if (!WriteFile(ChildInput.Write, Buf, AssertCast(Sz, Size), &Wr, NULL)) return -1; return Wr; #endif } diff --git a/src/common/Lgi/LgiMain.cpp b/src/common/Lgi/LgiMain.cpp --- a/src/common/Lgi/LgiMain.cpp +++ b/src/common/Lgi/LgiMain.cpp @@ -1,278 +1,278 @@ /** \file \author Matthew Allen */ #include "Lgi.h" #include "GToken.h" #ifdef LGI_SDL #include #endif #if defined(WINDOWS) && defined(_DEBUG) #include "crtdbg.h" #endif /** \brief The main entry point of a Lgi program To hide the differences between the different OS's standard entry points for programs the file LgiMain.cpp implements a simple platform specific entry point and then calls LgiMain() to pass execution onto your program. A simple LgiMain() looks like this: \code int LgiMain(OsAppArguments &Args) { GApp *App = new GApp("application/x-MyProgram", Args); if (App && App->IsOk()) { App->AppWnd = new MyWindow; App->Run(); delete App; } return 0; } \endcode */ extern int LgiMain ( /// The arguments passed in from the OS. OsAppArguments &AppArgs ); bool _BuildCheck() { #ifdef _DEBUG const char *AppBuild = "Debug"; #else const char *AppBuild = "Release"; #endif char *LgiBuild = (char*) (LgiIsReleaseBuild() ? "Release" : "Debug"); if (_stricmp(AppBuild, LgiBuild)) { LgiTrace("Build check failed, app=%s lgi=%s\n", AppBuild, LgiBuild); return LgiMsg( NULL, "This application and it's Dll's are mismatched:\n" "\n" " Application:\t%s\n" " Lgi:\t\t%s\n" "\n" "If you continue you may encounter crashes and other undesirable problems.\n" "Do you want to continue anyway?", "Warning", MB_YESNO, AppBuild, LgiBuild) == IDYES; } return true; } #ifdef WIN32 int #if _CONSOLE main(int args, char *arg[]) #else -WINAPI +WINAPI CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) #endif { #ifdef _DEBUG _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF); #endif int Status = 0; #if !_CONSOLE && WINNATIVE _lgi_app_instance = hInstance; #endif if (_BuildCheck()) { char16 *CL = (char16*)GetCommandLineW(); #if WINNATIVE OsAppArguments AppArgs; #if !_CONSOLE AppArgs.hInstance = hInstance; AppArgs.nCmdShow = nCmdShow; #endif if (*CL == '\"') { AppArgs.lpCmdLine = StrchrW(CL+1, '\"'); } else { AppArgs.lpCmdLine = StrchrW(CL+1, ' '); } if (AppArgs.lpCmdLine) AppArgs.lpCmdLine++; #else GArray Args; char16 *Ws = L" \t\r\n"; for (char16 *c = CL; *c; ) { while (*c && StrchrW(Ws, *c)) c++; if (*c == '\"' || *c == '\'') { char16 delim = *c++; char16 *end = StrchrW(c, delim); if (end) { Args.Add(WideToUtf8(c, end-c)); c = end + 1; } else { Args.Add(WideToUtf8(c)); break; } } else { char16 *end = c; while (*end && !StrchrW(Ws, *end)) end++; if (end > c) Args.Add(WideToUtf8(c, end-c)); c = end + (*end != 0); } } OsAppArguments AppArgs(Args.Length(), (const char**) &Args[0]); #endif Status = LgiMain(AppArgs); } return Status; } #else #if defined(MAC) && !COCOA pascal OSErr AppEventHandler(const AppleEvent *ae, AppleEvent *reply, SRefCon handlerRefcon) { OSErr err = eventNotHandledErr; LgiTrace("AppEventHandler called.\n"); if (ae->descriptorType == typeAppleEvent) { uint32 Code; Size Used = 0; DescType typeCode = 0; OSErr e = AEGetAttributePtr(ae, keyEventIDAttr, typeWildCard, &typeCode, &Code, sizeof(Code), &Used); if (e) { LgiTrace("%s:%i - AEGetAttributePtr failed (%i).\n", _FL, e); } else if (Code == kAEGetURL) { char urlbuf[512]; e = AEGetParamPtr(ae, keyDirectObject, typeUTF8Text, NULL, urlbuf, sizeof(urlbuf), &Used); if (e) LgiTrace("%s:%i - AEGetParamPtr failed (%i).\n", _FL, e); else if (Used < sizeof(urlbuf)) { urlbuf[Used] = 0; if (LgiApp && LgiApp->AppWnd) { LgiApp->AppWnd->PostEvent(M_URL, (GMessage::Param) new GString(urlbuf)); } else { LgiTrace("%s:%i - No AppWnd.\n", _FL); err = eventInternalErr; } } } else { LgiTrace("%s:%i - Support for '%4.4s' not implement.\n", _FL, &Code); err = eventParameterNotFoundErr; } } return err; } #endif int main(int Args, char **Arg) { int Status = 0; OsAppArguments AppArgs(Args, (const char**)Arg); #ifdef MAC #if 0 LgiTrace("Args=%i\n", Args); for (int i=0; i a; for (int i=0; i