// // Lol Engine // // Copyright © 2010—2015 Sam Hocevar // // Lol Engine is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it // and/or modify it under the terms of the Do What the Fuck You Want // to Public License, Version 2, as published by the WTFPL Task Force. // See http://www.wtfpl.net/ for more details. // #include #include "image-private.h" #include /* for std::swap */ namespace lol { /* HACK: We cannot make this an ImageLoader member function, because the * REGISTER_IMAGE_CODEC macro forward-declares free functions from * the "lol" namespace. An apparent bug in Visual Studio's compiler * makes it think these functions are actually in the top-level * namespace when the forward declaration is in a class member function. * To avoid the problem, we make the forward declaration in a free * function. * The bug was reported to Microsoft and fixed by them, but the fix * is not yet available. * https://connect.microsoft.com/VisualStudio/feedback/details/730878/ */ static bool RegisterAllCodecs(array &codeclist) { #if defined __ANDROID__ REGISTER_IMAGE_CODEC(AndroidImageCodec) #endif #if defined USE_GDIPLUS REGISTER_IMAGE_CODEC(GdiPlusImageCodec) #endif #if defined __APPLE__ && defined __MACH__ && defined __arm__ REGISTER_IMAGE_CODEC(IosImageCodec) #endif #if defined USE_SDL_IMAGE REGISTER_IMAGE_CODEC(SdlImageCodec) #endif #if defined USE_IMLIB2 REGISTER_IMAGE_CODEC(Imlib2ImageCodec) #endif REGISTER_IMAGE_CODEC(DummyImageCodec) REGISTER_IMAGE_CODEC(ZedImageCodec) REGISTER_IMAGE_CODEC(ZedPaletteImageCodec) REGISTER_IMAGE_CODEC(OricImageCodec) return true; } /* * Our static image loader */ static class ImageLoader { friend class Image; public: inline ImageLoader() { RegisterAllCodecs(m_codecs); } private: array m_codecs; map m_images; } g_image_loader; /* * Public Image class */ Image::Image() : m_data(new ImageData()) { } Image::Image(char const *path) : m_data(new ImageData()) { Load(path); } Image::Image(ivec2 size) : m_data(new ImageData()) { SetSize(size); } Image::Image (Image const &other) : m_data(new ImageData()) { Copy(other); } Image & Image::operator =(Image other) { /* Since the argument is passed by value, we’re assured it’s a new * object and we can safely swap our m_data pointers. */ std::swap(m_data, other.m_data); return *this; } Image::~Image() { for (int k : m_data->m_pixels.keys()) delete m_data->m_pixels[k]; delete m_data; } void Image::DummyFill() { //TODO: This is not very beautiful for (auto codec : g_image_loader.m_codecs) { if (String(codec->GetName()).contains("Dummy")) { codec->Load(this, nullptr); return; } } } void Image::Copy(uint8_t* pixels, ivec2 const& size, PixelFormat fmt) { ASSERT(fmt != PixelFormat::Unknown); SetSize(size); SetFormat(fmt); memcpy(m_data->m_pixels[(int)fmt]->data(), pixels, size.x * size.y * BytesPerPixel(fmt)); } void Image::Copy(Image const &other) { ivec2 size = other.GetSize(); PixelFormat fmt = other.GetFormat(); SetSize(size); if (fmt != PixelFormat::Unknown) { SetFormat(fmt); memcpy(m_data->m_pixels[(int)fmt]->data(), other.m_data->m_pixels[(int)fmt]->data(), size.x * size.y * BytesPerPixel(fmt)); } } bool Image::Load(char const *path) { ImageCodec* last_codec = nullptr; for (auto codec : g_image_loader.m_codecs) { last_codec = codec; if (codec->Load(this, path)) { msg::info("Image::Load: Codec %s succesfully loaded %s.\n", codec->GetName(), path); return true; } } //Log error, because we shouldn't be here msg::error("Image::Load: Last codec %s, Error loading image %s.\n", last_codec->GetName(), path); return false; } bool Image::Save(char const *path) { ImageCodec* last_codec = nullptr; for (auto codec : g_image_loader.m_codecs) { last_codec = codec; if (codec->Save(this, path)) { msg::info("Image::Save: Codec %s succesfully saved %s.\n", codec->GetName(), path); return true; } } //Log error, because we shouldn't be here msg::error("Image::Save: Last codec %s, Error saving image %s.\n", last_codec->GetName(), path); return false; } ivec2 Image::GetSize() const { return m_data->m_size; } void Image::SetSize(ivec2 size) { ASSERT(size.x > 0); ASSERT(size.y > 0); if (m_data->m_size != size) { for (int k : m_data->m_pixels.keys()) { delete m_data->m_pixels[k]; m_data->m_pixels[k] = nullptr; } m_data->m_format = PixelFormat::Unknown; } m_data->m_size = size; } /* Wrap-around mode for some operations */ WrapMode Image::GetWrapX() const { return m_data->m_wrap_x; } WrapMode Image::GetWrapY() const { return m_data->m_wrap_y; } void Image::SetWrap(WrapMode wrap_x, WrapMode wrap_y) { m_data->m_wrap_x = wrap_x; m_data->m_wrap_y = wrap_y; } /* The Lock() method */ template typename PixelType::type *Image::Lock() { SetFormat(T); return (typename PixelType::type *)m_data->m_pixels[(int)T]->data(); } /* The Lock2D() method */ void *Image::Lock2DHelper(PixelFormat T) { SetFormat(T); return m_data->m_pixels[(int)T]->data2d(); } template void Image::Unlock2D(array2d const &array) { ASSERT(m_data->m_pixels.has_key((int)m_data->m_format)); ASSERT(array.data() == m_data->m_pixels[(int)m_data->m_format]->data()); } /* Explicit specialisations for the above templates */ #define _T(T) \ template PixelType::type *Image::Lock(); \ template array2d::type> &Image::Lock2D(); \ template void Image::Unlock2D(array2d::type> const &array); _T(PixelFormat::Y_8) _T(PixelFormat::RGB_8) _T(PixelFormat::RGBA_8) _T(PixelFormat::Y_F32) _T(PixelFormat::RGB_F32) _T(PixelFormat::RGBA_F32) #undef _T /* Special case for the "any" format: return the last active buffer */ void *Image::Lock() { ASSERT(m_data->m_format != PixelFormat::Unknown); return m_data->m_pixels[(int)m_data->m_format]->data(); } void Image::Unlock(void const *pixels) { ASSERT(m_data->m_pixels.has_key((int)m_data->m_format)); ASSERT(pixels == m_data->m_pixels[(int)m_data->m_format]->data()); } bool Image::RetrieveTiles(array& tiles) const { /* TODO: re-implement this */ //return m_data->m_codecdata->RetrieveTiles(tiles); return false; } } /* namespace lol */