xupefei / APNG.NET

A fully-managed APNG parser, written in C#
MIT License
43 stars 8 forks source link

Loading up apng from program resources? #2

Open AraHaan opened 6 years ago

AraHaan commented 6 years ago

So, I got an C++ CLR Program that has tons of PNG iamges (some of them APNG's) And I want to have it where it defaults to the APNG and animite them in an Picturebox control (if possible) and also still be able to change the image and loop the APNG image as well after the last frame. Got any way to do it?

#pragma once
#include "LoadResources.h"
#include "resource.h"

namespace Project {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace LoadResources;

    /// <summary>
    /// Summary for ViewerForm.
    /// </summary>
    public ref class ViewerForm : public System::Windows::Forms::Form
    {
    public:
        ViewerForm(void)
        {
            this->ImageList = gcnew System::Collections::Generic::List<System::Drawing::Image ^>();
            for (int i = IDR_RCDATA1; i > IDR_RCDATA1; i++) {
                // populate the image list.
                System::Drawing::Image ^ item = GetImageResource(i, 10);
                if (item != nullptr) {
                    this->ImageList->Add(item);
                } else {
                    break;
                }
                delete item;
            }
            //this->_2000 = GetImageResource(IDR_RCDATA1, 10);
            //this->_2001 = GetImageResource(IDR_RCDATA2, 10);
            //this->_2002 = GetImageResource(IDR_RCDATA3, 10);
            //this->_2003 = GetImageResource(IDR_RCDATA4, 10);
            //this->_2004 = GetImageResource(IDR_RCDATA5, 10);
            //this->_2005 = GetImageResource(IDR_RCDATA6, 10);
            //this->_2006 = GetImageResource(IDR_RCDATA7, 10);
            //this->_2007 = GetImageResource(IDR_RCDATA8, 10);
            //this->_2008 = GetImageResource(IDR_RCDATA9, 10);
            //this->_2009 = GetImageResource(IDR_RCDATA10, 10);
            //this->_2010 = GetImageResource(IDR_RCDATA11, 10);
            InitializeComponent();
            //
            //TODO: Add constructor code here.
            //
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~ViewerForm()
        {
            if (components)
            {
                delete components;
            }
            if (this->ImageList)
            {
                // Clear the image list data.
                this->ImageList->Clear();
                delete this->ImageList;
            }
        }

    private: System::Windows::Forms::Button^  button1;
    protected:
    private: System::Windows::Forms::Button^  button2;
    private: System::Windows::Forms::PictureBox^  pictureBox1;
    private: System::Collections::Generic::List<System::Drawing::Image ^> ^ ImageList;

    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>
        System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->button1 = (gcnew System::Windows::Forms::Button());
            this->button2 = (gcnew System::Windows::Forms::Button());
            this->pictureBox1 = (gcnew System::Windows::Forms::PictureBox());
            (cli::safe_cast<System::ComponentModel::ISupportInitialize^>(this->pictureBox1))->BeginInit();
            this->SuspendLayout();
            // 
            // button1
            // 
            this->button1->Location = System::Drawing::Point(12, 364);
            this->button1->Name = L"button1";
            this->button1->Size = System::Drawing::Size(109, 36);
            this->button1->TabIndex = 0;
            this->button1->Text = L"<-- Prev.";
            this->button1->UseVisualStyleBackColor = true;
            this->button1->Click += gcnew System::EventHandler(this, &ViewerForm::button1_Click);
            // 
            // button2
            // 
            this->button2->Location = System::Drawing::Point(364, 364);
            this->button2->Name = L"button2";
            this->button2->Size = System::Drawing::Size(109, 36);
            this->button2->TabIndex = 1;
            this->button2->Text = L"Next -->";
            this->button2->UseVisualStyleBackColor = true;
            this->button2->Click += gcnew System::EventHandler(this, &ViewerForm::button2_Click);
            // 
            // pictureBox1
            // 
            this->pictureBox1->Location = System::Drawing::Point(12, 9);
            this->pictureBox1->Name = L"pictureBox1";
            this->pictureBox1->Size = System::Drawing::Size(461, 349);
            this->pictureBox1->SizeMode = System::Windows::Forms::PictureBoxSizeMode::StretchImage;
            this->pictureBox1->TabIndex = 2;
            this->pictureBox1->TabStop = false;
            // 
            // ViewerForm
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(485, 409);
            this->Controls->Add(this->pictureBox1);
            this->Controls->Add(this->button2);
            this->Controls->Add(this->button1);
            this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedSingle;
            this->Name = L"ViewerForm";
            this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
            this->Text = L"ViewerForm";
            this->Load += gcnew System::EventHandler(this, &ViewerForm::ViewerForm_Load);
            (cli::safe_cast<System::ComponentModel::ISupportInitialize^>(this->pictureBox1))->EndInit();
            this->ResumeLayout(false);

        }

#pragma endregion

    private: System::Void ViewerForm_Load(System::Object^  sender, System::EventArgs^  e) {
        this->Icon = GetIconResource(IDI_MAINICON);
        this->pictureBox1->Image = this->ImageList[0];
        this->button1->Enabled = false;
    }

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
        for (int i = this->ImageList->Count; i < this->ImageList->Capacity; i--)
        {
            if (i == 1)
            {
                this->pictureBox1->Image = this->ImageList[0];
                this->button1->Enabled = false;
            }
            else if ((this->pictureBox1->Image == this->ImageList[i]) && i < this->ImageList->Count)
            {
                this->pictureBox1->Image = this->ImageList[i - 1];
            }
            else
            {
                this->pictureBox1->Image = this->ImageList[i - 1];
                this->button2->Enabled = true;
            }
        }
    }

    private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
        for (int i = 0; i < this->ImageList->Count; i++)
        {
            if (i == 0)
            {
                this->pictureBox1->Image = this->ImageList[1];
                this->button1->Enabled = true;
            }
            else if ((this->pictureBox1->Image == this->ImageList[i]) && i < this->ImageList->Count - 1)
            {
                this->pictureBox1->Image = this->ImageList[i + 1];
            }
            else
            {
                this->pictureBox1->Image = this->ImageList[i + 1];
                this->button2->Enabled = false;
            }
        }
    }
};
}

And yes I wrote it to auto adjust the viewer form based on now many images are present in an *.rc file that then compiles into an *.res file, which then in return ends up in the binary's .rsrc section so that way I dont have to manually edit the code and make it harder to update and fix an issue.

Also LoadResources.cpp:

#include <Windows.h>
#include "LoadResources.h"

using namespace System::Runtime::InteropServices;

namespace LoadResources {
    System::Drawing::Image ^ GetImageResource(int resource, int type) {
        HRSRC image_resource = FindResource(
            NULL, MAKEINTRESOURCE(resource), MAKEINTRESOURCE(type));
        unsigned int image_size = SizeofResource(NULL, image_resource);
        HGLOBAL image_global = LoadResource(NULL, image_resource);
        if (image_global == nullptr) {
            return nullptr;
        }
        cli::array<BYTE> ^MemPtr = gcnew array<BYTE>(image_size + 2);
        char * p_imagedata = (char *)LockResource(image_global);
        Marshal::Copy((System::IntPtr)p_imagedata, MemPtr, 0, image_size);
        System::IO::MemoryStream ^stream = gcnew System::IO::MemoryStream(MemPtr);
        stream->Write(MemPtr, 0, image_size);
        stream->Position = 0;
        System::Drawing::Image ^ptr_image = System::Drawing::Image::FromStream(stream);
        return  ptr_image;
    }

    System::Drawing::Icon ^ GetIconResource(int resource) {
        HICON hIcon = (HICON)LoadImage(GetModuleHandle(nullptr), MAKEINTRESOURCE(resource), IMAGE_ICON, 16, 16, LR_SHARED);
        System::Drawing::Icon^ ico;
        if (hIcon) {
            ico = System::Drawing::Icon::FromHandle((System::IntPtr)hIcon);
        }
        return ico;
    }
}
xupefei commented 6 years ago

from program resources

Loading image from resources should not be a problem since APNG accepts byte array in the constructor.

anim[a]te them in an Picturebox control

This is not a simple assignment. You will need to provider a timer which modifies the Picturebox's content at appropriate time. In WPF this can be done by binding an ObjectAnimationUsingKeyFrames to an Image control (related code available here), but I am not sure how to do the same for Winforms.