diff --git a/build/lol-build b/build/lol-build index 2f290b9b..6c05422e 100755 --- a/build/lol-build +++ b/build/lol-build @@ -288,6 +288,14 @@ build() { cd "$top_srcdir" case "$platform" in + win*-i386) + # Because of locking issues in Wine’s winepath.exe, we only + # build the static libraries in parallel. + make -j$LOL_PARALLEL -C src/bullet + make -j$LOL_PARALLEL -C src/lua + make -j$LOL_PARALLEL -C src/ liblolcore.a + make + ;; *) make -j$LOL_PARALLEL ;; diff --git a/src/image/codec/gdiplus-image.cpp b/src/image/codec/gdiplus-image.cpp index 0566eaf3..1b053fde 100644 --- a/src/image/codec/gdiplus-image.cpp +++ b/src/image/codec/gdiplus-image.cpp @@ -38,6 +38,7 @@ class GdiPlusImageCodec : public ImageCodec public: virtual bool Load(Image *image, char const *path); virtual bool Save(Image *image, char const *path); + virtual bool Save(array const &images, char const *path); }; DECLARE_IMAGE_CODEC(GdiPlusImageCodec, 100) @@ -54,9 +55,7 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) status = Gdiplus::GdiplusStartup(&token, &input, nullptr); if (status != Gdiplus::Ok) { -#if !LOL_BUILD_RELEASE Log::Error("error %d while initialising GDI+\n", status); -#endif return false; } @@ -69,9 +68,7 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) wchar_t *wpath = new wchar_t[len + 1]; if (mbstowcs(wpath, fullpath.C(), len + 1) == (size_t)-1) { -#if !LOL_BUILD_RELEASE Log::Error("invalid image name %s\n", fullpath.C()); -#endif delete[] wpath; continue; } @@ -84,11 +81,9 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) status = bitmap->GetLastStatus(); if (status != Gdiplus::Ok) { -#if !LOL_BUILD_RELEASE if (status != Gdiplus::InvalidParameter) Log::Error("error %d loading %s\n", status, fullpath.C()); -#endif delete bitmap; bitmap = nullptr; } @@ -101,9 +96,7 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) if (!bitmap) { -#if !LOL_BUILD_RELEASE Log::Error("could not load %s\n", path); -#endif return false; } @@ -113,9 +106,7 @@ bool GdiPlusImageCodec::Load(Image *image, char const *path) if (bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) { -#if !LOL_BUILD_RELEASE Log::Error("could not lock bits in %s\n", path); -#endif delete bitmap; return false; } @@ -187,12 +178,8 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) ivec2 size = image->GetSize(); - /* FIXME: we create a new image because there is no guarantee that - * the image comes from GDI. But the engine architecture doesn't - * allow us to save other images anyway. */ Gdiplus::Bitmap *b = new Gdiplus::Bitmap(size.x, size.y, PixelFormat32bppARGB); - Gdiplus::BitmapData bdata; Gdiplus::Rect rect(0, 0, size.x, size.y); @@ -230,6 +217,116 @@ bool GdiPlusImageCodec::Save(Image *image, char const *path) return true; } +bool GdiPlusImageCodec::Save(array const &images, char const *path) +{ + ULONG_PTR token; + Gdiplus::GdiplusStartupInput input; + Gdiplus::GdiplusStartup(&token, &input, nullptr); + + wchar_t const *fmt = L"image/gif"; + + unsigned int num = 0, encoder_size = 0; + Gdiplus::GetImageEncodersSize(&num, &encoder_size); + if (num == 0) + { + Log::Error("no GDI+ image encoders found\n"); + return false; + } + Gdiplus::ImageCodecInfo *codecs + = (Gdiplus::ImageCodecInfo *)new uint8_t[encoder_size]; + Gdiplus::GetImageEncoders(num, encoder_size, codecs); + CLSID clsid; + for (unsigned int i = 0; i < num; i++) + { + if (wcscmp(codecs[i].MimeType, fmt) == 0) + { + clsid = codecs[i].Clsid; + break; + } + } + + // FIXME: clsid can be uninitialised + + size_t len; + len = mbstowcs(nullptr, path, 0); + wchar_t *wpath = new wchar_t[len + 1]; + if (mbstowcs(wpath, path, len + 1) == (size_t)-1) + { + Log::Error("could not convert GDI+ filename '%s' to widechar\n", path); + delete[] wpath; + delete[] codecs; + return false; + } + + Gdiplus::Image *root_image = nullptr; + +#if 0 + Gdiplus::Bitmap *tmp = new Gdiplus::Bitmap(1, 1, PixelFormat32bppARGB); + unsigned int param_count = tmp->GetEncoderParameterListSize(&clsid); + printf("%d parameters\n", param_count); + delete tmp; +#endif + + for (int i = 0; i < images.Count(); ++i) + { + ivec2 size = images[i]->GetSize(); + + Gdiplus::Bitmap *b = new Gdiplus::Bitmap(size.x, size.y, + PixelFormat32bppARGB); + Gdiplus::BitmapData bdata; + Gdiplus::Rect rect(0, 0, size.x, size.y); + + if (b->LockBits(&rect, (unsigned int)Gdiplus::ImageLockModeWrite, + PixelFormat32bppARGB, &bdata) != Gdiplus::Ok) + { + Log::Error("could not lock GDI+ image bits\n"); + delete b; + delete[] wpath; + delete[] codecs; + return false; + } + + u8vec4 *psrc = images[i]->Lock(); + u8vec4 *psrc0 = psrc; + u8vec4 *pdst = static_cast(bdata.Scan0); + for (int y = 0; y < size.y; y++) + for (int x = 0; x < size.x; x++) + *pdst++ = (*psrc++).bgra; + images[i]->Unlock(psrc0); + b->UnlockBits(&bdata); + + Gdiplus::Status ret; + if (i == 0) + { + root_image = b; + b = nullptr; + ret = root_image->Save(wpath, &clsid, nullptr); + } + else + { + ret = root_image->SaveAdd(b, nullptr); + } + + if (ret != Gdiplus::Ok) + { + Log::Error("could not save GDI+ image '%s'\n", path); + delete[] wpath; + delete[] codecs; + delete root_image; + delete b; + return false; + } + + delete b; + } + + delete[] wpath; + delete[] codecs; + delete root_image; + + return true; +} + } /* namespace lol */ #endif /* defined USE_GDIPLUS */ diff --git a/src/image/image-private.h b/src/image/image-private.h index 902bdb91..0ba5c81b 100644 --- a/src/image/image-private.h +++ b/src/image/image-private.h @@ -72,6 +72,12 @@ public: virtual bool Load(Image *image, char const *path) = 0; virtual bool Save(Image *image, char const *path) = 0; + /* TODO: For animated GIFs. Maybe move this to a Movie object instead? */ + virtual bool Save(array const &images, char const *path) + { + return false; + } + /* TODO: this should become more fine-grained */ int m_priority; }; diff --git a/src/image/image.cpp b/src/image/image.cpp index 381755cd..41e8dda7 100644 --- a/src/image/image.cpp +++ b/src/image/image.cpp @@ -144,6 +144,14 @@ bool Image::Save(char const *path) return false; } +bool Image::Save(array const &images, char const *path) +{ + for (auto codec : g_image_loader.m_codecs) + if (codec->Save(images, path)) + return true; + return false; +} + ivec2 Image::GetSize() const { return m_data->m_size; diff --git a/src/lol/image/image.h b/src/lol/image/image.h index be45b1cc..f770bed0 100644 --- a/src/lol/image/image.h +++ b/src/lol/image/image.h @@ -72,6 +72,7 @@ public: bool Load(char const *path); bool Save(char const *path); + static bool Save(array const &images, char const *path); /* Low level access */ ivec2 GetSize() const;