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
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
114 changes: 114 additions & 0 deletions Types/JPG/include/jpg.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#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;
constexpr uint16 JPG_SOF1_MARKER = 0xC1FF;
constexpr uint16 JPG_SOF2_MARKER = 0xC2FF;
constexpr uint16 JPG_SOF3_MARKER = 0xC3FF;
constexpr uint8 JPG_START_MAKER_BYTE = 0xFF;
constexpr uint8 JPG_SOS_BYTE = 0xDA;
constexpr uint8 JPG_EOI_BYTE = 0xD9;


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)
51 changes: 51 additions & 0 deletions Types/JPG/src/JPGFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#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 || marker == JPG::JPG_SOF1_MARKER ||
marker == JPG::JPG_SOF2_MARKER || marker == JPG::JPG_SOF3_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(bf), false, "");

return true;
}
66 changes: 66 additions & 0 deletions Types/JPG/src/PanelInformation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#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) });

const auto xDensity = Endian::BigToNative(jpg->app0MarkerSegment.xDensity);
const auto yDensity = Endian::BigToNative(jpg->app0MarkerSegment.yDensity);
general->AddItem({ "X Density", tempStr.Format("%u", xDensity)});
general->AddItem({ "Y Density", tempStr.Format("%u", yDensity)});

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();
}
Loading
Loading