Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JPG specific plugin #322

Merged
merged 10 commits into from
Jul 26, 2024
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ if(NOT DEFINED CMAKE_TESTING_ENABLED)
add_subdirectory(Types/SQLite)
add_subdirectory(Types/JCLASS)
add_subdirectory(Types/EML)
add_subdirectory(Types/JPG)

# Generic plugins supported by GView
add_subdirectory(GenericPlugins/CharacterTable)
Expand Down
2 changes: 2 additions & 0 deletions Types/JPG/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include(type)
create_type(JPG)
107 changes: 107 additions & 0 deletions Types/JPG/include/jpg.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#pragma once

#include "GView.hpp"

namespace GView
{
namespace Type
{
namespace JPG
{
#pragma pack(push, 2)

constexpr uint16 JPG_SOI_MARKER = 0xD8FF;
constexpr uint16 JPG_EOI_MARKER = 0xD9FF;
constexpr uint16 JPG_APP0_MARKER = 0xE0FF;
constexpr uint16 JPG_SOF0_MARKER = 0xC0FF;

struct Header {
uint16 soi; // Start of Image marker
uint16 app0; // APP0 marker
};

struct App0MarkerSegment {
uint16 length;
char identifier[5]; // "JFIF" null-terminated
uint8 version[2];
uint8 densityUnits;
uint16 xDensity;
uint16 yDensity;
uint8 xThumbnail;
uint8 yThumbnail;
};

struct SOF0MarkerSegment {
uint16 height;
uint16 width;
};

#pragma pack(pop) // Back to default packing

class JPGFile : public TypeInterface, public View::ImageViewer::LoadImageInterface
{
public:
Header header{};
App0MarkerSegment app0MarkerSegment{};
SOF0MarkerSegment sof0MarkerSegment{};

Reference<GView::Utils::SelectionZoneInterface> selectionZoneInterface;

public:
JPGFile();
virtual ~JPGFile()
{
}

bool Update();

std::string_view GetTypeName() override
{
return "JPG";
}
void RunCommand(std::string_view) override
{
}

bool LoadImageToObject(Image& img, uint32 index) override;

uint32 GetSelectionZonesCount() override
{
CHECK(selectionZoneInterface.IsValid(), 0, "");
return selectionZoneInterface->GetSelectionZonesCount();
}

TypeInterface::SelectionZone GetSelectionZone(uint32 index) override
{
static auto d = TypeInterface::SelectionZone{ 0, 0 };
CHECK(selectionZoneInterface.IsValid(), d, "");
CHECK(index < selectionZoneInterface->GetSelectionZonesCount(), d, "");

return selectionZoneInterface->GetSelectionZone(index);
}
};
namespace Panels
{
class Information : public AppCUI::Controls::TabPage
{
Reference<GView::Type::JPG::JPGFile> jpg;
Reference<AppCUI::Controls::ListView> general;
Reference<AppCUI::Controls::ListView> issues;

void UpdateGeneralInformation();
void UpdateIssues();
void RecomputePanelsPositions();

public:
Information(Reference<GView::Type::JPG::JPGFile> jpg);

void Update();
virtual void OnAfterResize(int newWidth, int newHeight) override
{
RecomputePanelsPositions();
}
};
}; // namespace Panels
} // namespace JPG
} // namespace Type
} // namespace GView
4 changes: 4 additions & 0 deletions Types/JPG/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target_sources(JPG PRIVATE
jpg.cpp
JPGFile.cpp
PanelInformation.cpp)
50 changes: 50 additions & 0 deletions Types/JPG/src/JPGFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "jpg.hpp"

using namespace GView::Type::JPG;

JPGFile::JPGFile()
{
}

bool JPGFile::Update()
{
memset(&header, 0, sizeof(header));
memset(&app0MarkerSegment, 0, sizeof(app0MarkerSegment));
memset(&sof0MarkerSegment, 0, sizeof(sof0MarkerSegment));

auto& data = this->obj->GetData();

CHECK(data.Copy<Header>(0, header), false, "");
CHECK(data.Copy<App0MarkerSegment>(sizeof(Header), app0MarkerSegment), false, "");

uint64 offset = sizeof(Header) + sizeof(App0MarkerSegment);
bool found = false;
while (offset < data.GetSize())
{
uint16 marker;
CHECK(data.Copy<uint16>(offset, marker), false, "");
// get the width and height
if (marker == JPG::JPG_SOF0_MARKER)
{
CHECK(data.Copy<SOF0MarkerSegment>(offset + 5, sof0MarkerSegment), false, "");
found = true;
break;
}
offset += 1;
}
return found;
}

bool JPGFile::LoadImageToObject(Image& img, uint32 index)
{
Buffer buf;
auto bf = obj->GetData().GetEntireFile();
if (bf.IsValid() == false) {
buf = this->obj->GetData().CopyEntireFile();
CHECK(buf.IsValid(), false, "Fail to copy Entire file");
bf = (BufferView) buf;
}
CHECK(img.Create(buf), false, "");

return true;
}
62 changes: 62 additions & 0 deletions Types/JPG/src/PanelInformation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include "jpg.hpp"

using namespace GView::Type::JPG;
using namespace AppCUI::Controls;

Panels::Information::Information(Reference<GView::Type::JPG::JPGFile> _jpg) : TabPage("&Information")
{
jpg = _jpg;
general = Factory::ListView::Create(this, "x:0,y:0,w:100%,h:10", { "n:Field,w:12", "n:Value,w:100" }, ListViewFlags::None);

issues = Factory::ListView::Create(this, "x:0,y:21,w:100%,h:10", { "n:Info,w:200" }, ListViewFlags::HideColumns);

this->Update();
}

void Panels::Information::UpdateGeneralInformation()
{
LocalString<256> tempStr;
NumericFormatter n;

general->DeleteAllItems();
general->AddItem("File");
// size
general->AddItem({ "Size", tempStr.Format("%s bytes", n.ToString(jpg->obj->GetData().GetSize(), { NumericFormatFlags::None, 10, 3, ',' }).data()) });
// Size
const auto width = Endian::BigToNative(jpg->sof0MarkerSegment.width);
const auto height = Endian::BigToNative(jpg->sof0MarkerSegment.height);
general->AddItem({ "Size", tempStr.Format("%u x %u", width, height) });

// extra info
/* general->AddItem({ "Density Units", tempStr.Format("%u", jpg->app0MarkerSegment.densityUnits) });
general->AddItem({ "X Density", tempStr.Format("%u", jpg->app0MarkerSegment.xDensity >> 8) });
general->AddItem({ "Y Density", tempStr.Format("%u", jpg->app0MarkerSegment.yDensity >> 8) });
general->AddItem({ "X Thumbnail", tempStr.Format("%u", jpg->app0MarkerSegment.xThumbnail) });
general->AddItem({ "Y Thumbnail", tempStr.Format("%u", jpg->app0MarkerSegment.yThumbnail) });
*/
}


void Panels::Information::UpdateIssues()
{
}

void Panels::Information::RecomputePanelsPositions()
{
int py = 0;
int w = this->GetWidth();
int h = this->GetHeight();

if ((!general.IsValid()) || (!issues.IsValid()))
return;

issues->SetVisible(false);
this->general->Resize(w, h);
}

void Panels::Information::Update()
{
UpdateGeneralInformation();
UpdateIssues();
RecomputePanelsPositions();
}
77 changes: 77 additions & 0 deletions Types/JPG/src/jpg.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "jpg.hpp"

using namespace AppCUI;
using namespace AppCUI::Utils;
using namespace AppCUI::Application;
using namespace AppCUI::Controls;
using namespace GView::Utils;
using namespace GView::Type;
using namespace GView;
using namespace GView::View;

extern "C"
{
PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::string_view& extension)
{
if (buf.GetLength() < sizeof(JPG::Header) + sizeof(JPG::App0MarkerSegment))
return false;
auto header = buf.GetObject<JPG::Header>();
if (header->soi != JPG::JPG_SOI_MARKER || header->app0 != JPG::JPG_APP0_MARKER)
return false;
auto app0MarkerSegment = buf.GetObject<JPG::App0MarkerSegment>(sizeof(JPG::Header));
if (memcmp(app0MarkerSegment->identifier, "JFIF", 5) != 0)
return false;
// all good
return true;
}

PLUGIN_EXPORT TypeInterface* CreateInstance()
{
return new JPG::JPGFile;
}

void CreateBufferView(Reference<GView::View::WindowInterface> win, Reference<JPG::JPGFile> jpg)
{
BufferViewer::Settings settings;

settings.AddZone(0, sizeof(JPG::Header), ColorPair{ Color::Magenta, Color::DarkBlue }, "Header");
settings.AddZone(sizeof(JPG::Header), sizeof(JPG::App0MarkerSegment), ColorPair{ Color::Olive, Color::DarkBlue }, "APP0 Marker Segment");
gheorghitamutu marked this conversation as resolved.
Show resolved Hide resolved

jpg->selectionZoneInterface = win->GetSelectionZoneInterfaceFromViewerCreation(settings);
}

void CreateImageView(Reference<GView::View::WindowInterface> win, Reference<JPG::JPGFile> jpg)
{
GView::View::ImageViewer::Settings settings;
settings.SetLoadImageCallback(jpg.ToBase<View::ImageViewer::LoadImageInterface>());
settings.AddImage(0, jpg->obj->GetData().GetSize());
win->CreateViewer(settings);
}

PLUGIN_EXPORT bool PopulateWindow(Reference<GView::View::WindowInterface> win)
{
auto jpg = win->GetObject()->GetContentType<JPG::JPGFile>();
jpg->Update();

// add viewer
CreateImageView(win, jpg);
CreateBufferView(win, jpg);

// add panels
win->AddPanel(Pointer<TabPage>(new JPG::Panels::Information(jpg)), true);

return true;
}

PLUGIN_EXPORT void UpdateSettings(IniSection sect)
{
sect["Pattern"] = "magic:FF D8";
sect["Priority"] = 1;
sect["Description"] = "JPEG image file (*.jpg, *.jpeg)";
}
}

int main()
{
return 0;
}
Loading