From 0016dd9e1782105530dbfb85dfa1cb4d47c034bd Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Mon, 22 Jul 2024 18:21:02 +0300 Subject: [PATCH 1/8] added type jpg with no working functionality for imageview --- CMakeLists.txt | 1 + Types/JPG/CMakeLists.txt | 2 + Types/JPG/include/jpg.hpp | 110 +++++++++++++++++++++++++++++ Types/JPG/src/CMakeLists.txt | 4 ++ Types/JPG/src/JPGFile.cpp | 56 +++++++++++++++ Types/JPG/src/PanelInformation.cpp | 60 ++++++++++++++++ Types/JPG/src/jpg.cpp | 77 ++++++++++++++++++++ 7 files changed, 310 insertions(+) create mode 100644 Types/JPG/CMakeLists.txt create mode 100644 Types/JPG/include/jpg.hpp create mode 100644 Types/JPG/src/CMakeLists.txt create mode 100644 Types/JPG/src/JPGFile.cpp create mode 100644 Types/JPG/src/PanelInformation.cpp create mode 100644 Types/JPG/src/jpg.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index eaf4ae4a..98f40a24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/Types/JPG/CMakeLists.txt b/Types/JPG/CMakeLists.txt new file mode 100644 index 00000000..2222a093 --- /dev/null +++ b/Types/JPG/CMakeLists.txt @@ -0,0 +1,2 @@ +include(type) +create_type(JPG) diff --git a/Types/JPG/include/jpg.hpp b/Types/JPG/include/jpg.hpp new file mode 100644 index 00000000..0a93476a --- /dev/null +++ b/Types/JPG/include/jpg.hpp @@ -0,0 +1,110 @@ +#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 length; + uint8 precision; + uint16 height; + uint16 width; + uint8 numberOfComponents; + }; + +#pragma pack(pop) // Back to default packing + + class JPGFile : public TypeInterface, public View::ImageViewer::LoadImageInterface + { + public: + Header header{}; + App0MarkerSegment app0MarkerSegment{}; + SOF0MarkerSegment sof0MarkerSegment{}; + + Reference 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 jpg; + Reference general; + Reference issues; + + void UpdateGeneralInformation(); + void UpdateIssues(); + void RecomputePanelsPositions(); + + public: + Information(Reference jpg); + + void Update(); + virtual void OnAfterResize(int newWidth, int newHeight) override + { + RecomputePanelsPositions(); + } + }; + }; // namespace Panels + } // namespace JPG +} // namespace Type +} // namespace GView \ No newline at end of file diff --git a/Types/JPG/src/CMakeLists.txt b/Types/JPG/src/CMakeLists.txt new file mode 100644 index 00000000..826602ab --- /dev/null +++ b/Types/JPG/src/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(JPG PRIVATE + jpg.cpp + JPGFile.cpp + PanelInformation.cpp) \ No newline at end of file diff --git a/Types/JPG/src/JPGFile.cpp b/Types/JPG/src/JPGFile.cpp new file mode 100644 index 00000000..d86801a3 --- /dev/null +++ b/Types/JPG/src/JPGFile.cpp @@ -0,0 +1,56 @@ +#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(); + + if (!data.Copy
(0, header)) + return false; + if (!data.Copy(sizeof(Header), app0MarkerSegment)) + return false; + + uint64 offset = sizeof(Header) + sizeof(App0MarkerSegment); + while (offset < data.GetSize()) + { + uint16 marker; + if (!data.Copy(offset, marker)) + return false; + if (marker == JPG_SOF0_MARKER) + { + if (!data.Copy(offset + 2, sof0MarkerSegment)) + return false; + break; + } + offset += 2; + uint16 segmentLength; + if (!data.Copy(offset, segmentLength)) + return false; + offset += segmentLength; + } + + return true; +} + +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; +} \ No newline at end of file diff --git a/Types/JPG/src/PanelInformation.cpp b/Types/JPG/src/PanelInformation.cpp new file mode 100644 index 00000000..1b940d42 --- /dev/null +++ b/Types/JPG/src/PanelInformation.cpp @@ -0,0 +1,60 @@ +#include "jpg.hpp" + +using namespace GView::Type::JPG; +using namespace AppCUI::Controls; + +Panels::Information::Information(Reference _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 + general->AddItem({ "Size", tempStr.Format("%u x %u", jpg->sof0MarkerSegment.width, jpg->sof0MarkerSegment.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(); +} diff --git a/Types/JPG/src/jpg.cpp b/Types/JPG/src/jpg.cpp new file mode 100644 index 00000000..adf9be84 --- /dev/null +++ b/Types/JPG/src/jpg.cpp @@ -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(); + if (header->soi != JPG::JPG_SOI_MARKER || header->app0 != JPG::JPG_APP0_MARKER) + return false; + auto app0MarkerSegment = buf.GetObject(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 win, Reference 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"); + + jpg->selectionZoneInterface = win->GetSelectionZoneInterfaceFromViewerCreation(settings); + } + + void CreateImageView(Reference win, Reference jpg) + { + GView::View::ImageViewer::Settings settings; + settings.SetLoadImageCallback(jpg.ToBase()); + settings.AddImage(0, jpg->obj->GetData().GetSize()); + win->CreateViewer(settings); + } + + PLUGIN_EXPORT bool PopulateWindow(Reference win) + { + auto jpg = win->GetObject()->GetContentType(); + jpg->Update(); + + // add viewer + CreateImageView(win, jpg); + CreateBufferView(win, jpg); + + // add panels + win->AddPanel(Pointer(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; +} \ No newline at end of file From 41770af77e03f9daa37301706a2bcfb5122a6a9c Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Wed, 24 Jul 2024 15:49:11 +0300 Subject: [PATCH 2/8] fixed size u x u for JPG SOF0 marker --- Types/JPG/include/jpg.hpp | 3 --- Types/JPG/src/JPGFile.cpp | 26 ++++++++++---------------- Types/JPG/src/PanelInformation.cpp | 4 +++- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Types/JPG/include/jpg.hpp b/Types/JPG/include/jpg.hpp index 0a93476a..518504c3 100644 --- a/Types/JPG/include/jpg.hpp +++ b/Types/JPG/include/jpg.hpp @@ -32,11 +32,8 @@ namespace Type }; struct SOF0MarkerSegment { - uint16 length; - uint8 precision; uint16 height; uint16 width; - uint8 numberOfComponents; }; #pragma pack(pop) // Back to default packing diff --git a/Types/JPG/src/JPGFile.cpp b/Types/JPG/src/JPGFile.cpp index d86801a3..1c621064 100644 --- a/Types/JPG/src/JPGFile.cpp +++ b/Types/JPG/src/JPGFile.cpp @@ -14,31 +14,25 @@ bool JPGFile::Update() auto& data = this->obj->GetData(); - if (!data.Copy
(0, header)) - return false; - if (!data.Copy(sizeof(Header), app0MarkerSegment)) - return false; + CHECK(data.Copy
(0, header), false, ""); + CHECK(data.Copy(sizeof(Header), app0MarkerSegment), false, ""); uint64 offset = sizeof(Header) + sizeof(App0MarkerSegment); + bool found = false; while (offset < data.GetSize()) { uint16 marker; - if (!data.Copy(offset, marker)) - return false; - if (marker == JPG_SOF0_MARKER) + CHECK(data.Copy(offset, marker), false, ""); + // get the width and height + if (marker == JPG::JPG_SOF0_MARKER) { - if (!data.Copy(offset + 2, sof0MarkerSegment)) - return false; + CHECK(data.Copy(offset + 5, sof0MarkerSegment), false, ""); + found = true; break; } - offset += 2; - uint16 segmentLength; - if (!data.Copy(offset, segmentLength)) - return false; - offset += segmentLength; + offset += 1; } - - return true; + return found; } bool JPGFile::LoadImageToObject(Image& img, uint32 index) diff --git a/Types/JPG/src/PanelInformation.cpp b/Types/JPG/src/PanelInformation.cpp index 1b940d42..53d09c4f 100644 --- a/Types/JPG/src/PanelInformation.cpp +++ b/Types/JPG/src/PanelInformation.cpp @@ -23,7 +23,9 @@ void Panels::Information::UpdateGeneralInformation() // size general->AddItem({ "Size", tempStr.Format("%s bytes", n.ToString(jpg->obj->GetData().GetSize(), { NumericFormatFlags::None, 10, 3, ',' }).data()) }); // Size - general->AddItem({ "Size", tempStr.Format("%u x %u", jpg->sof0MarkerSegment.width, jpg->sof0MarkerSegment.height) }); + 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) }); From 1d85d38aa32c1b424fe97c4af86af0f9a97819a1 Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Wed, 24 Jul 2024 19:48:36 +0300 Subject: [PATCH 3/8] jpg AppCUI image loader --- AppCUI | 2 +- vcpkg.json | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/AppCUI b/AppCUI index fad200a4..6bf1655f 160000 --- a/AppCUI +++ b/AppCUI @@ -1 +1 @@ -Subproject commit fad200a496427aceab5598c785fb3c9ff58ea17f +Subproject commit 6bf1655f8317c4333e568ebd08409c9eb42b23d1 diff --git a/vcpkg.json b/vcpkg.json index cfce5468..31fa3d90 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -63,7 +63,11 @@ { "name": "re2", "platform": "windows | linux | osx" - } + }, + { + "name": "libjpeg-turbo", + "platform": "windows | linux | osx" + } ], "vcpkg-configuration": { "overlay-ports": [ "./AppCUI/vcpkg-ports/ncurses"] From 4594b446ad72c5c73341166123ae1b57160d8258 Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Fri, 26 Jul 2024 12:19:48 +0300 Subject: [PATCH 4/8] [jpg] updated the bufferview, fixed bug for size --- Types/JPG/include/jpg.hpp | 3 ++ Types/JPG/src/JPGFile.cpp | 5 +-- Types/JPG/src/jpg.cpp | 76 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/Types/JPG/include/jpg.hpp b/Types/JPG/include/jpg.hpp index 518504c3..d12fa1be 100644 --- a/Types/JPG/include/jpg.hpp +++ b/Types/JPG/include/jpg.hpp @@ -14,6 +14,9 @@ namespace Type 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; struct Header { uint16 soi; // Start of Image marker diff --git a/Types/JPG/src/JPGFile.cpp b/Types/JPG/src/JPGFile.cpp index 1c621064..14aee436 100644 --- a/Types/JPG/src/JPGFile.cpp +++ b/Types/JPG/src/JPGFile.cpp @@ -24,7 +24,8 @@ bool JPGFile::Update() uint16 marker; CHECK(data.Copy(offset, marker), false, ""); // get the width and height - if (marker == JPG::JPG_SOF0_MARKER) + if (marker == JPG::JPG_SOF0_MARKER || marker == JPG::JPG_SOF1_MARKER || + marker == JPG::JPG_SOF2_MARKER || marker == JPG::JPG_SOF3_MARKER) { CHECK(data.Copy(offset + 5, sof0MarkerSegment), false, ""); found = true; @@ -44,7 +45,7 @@ bool JPGFile::LoadImageToObject(Image& img, uint32 index) CHECK(buf.IsValid(), false, "Fail to copy Entire file"); bf = (BufferView) buf; } - CHECK(img.Create(buf), false, ""); + CHECK(img.Create(bf), false, ""); return true; } \ No newline at end of file diff --git a/Types/JPG/src/jpg.cpp b/Types/JPG/src/jpg.cpp index adf9be84..b14c9e7d 100644 --- a/Types/JPG/src/jpg.cpp +++ b/Types/JPG/src/jpg.cpp @@ -34,8 +34,80 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str { 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"); + const std::vector colors = { ColorPair{ Color::Teal, Color::DarkBlue }, ColorPair{ Color::Yellow, Color::DarkBlue } }; + + auto& data = jpg->obj->GetData(); + const uint64 dataSize = data.GetSize(); + uint64 offset = 0; + uint32 colorIndex = 0; + uint32 segmentCount = 1; + + settings.AddZone(0, sizeof(JPG::Header), ColorPair{ Color::Magenta, Color::DarkBlue }, "SOI Segment"); + offset += sizeof(JPG::Header); + + settings.AddZone(offset, sizeof(JPG::App0MarkerSegment), ColorPair{ Color::Olive, Color::DarkBlue }, "APP0 Segment"); + offset += sizeof(JPG::App0MarkerSegment); + + while (offset < dataSize - 2) { + uint8 marker_prefix; + uint8 marker_type; + + if (!data.Copy(offset, marker_prefix) || marker_prefix != 0xFF) { + break; + } + + if (!data.Copy(offset + 1, marker_type) || marker_type == 0x00 || marker_type == 0xFF) { + offset++; + continue; + } + + if (marker_type == 0xD9) { + settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); + break; + } + + uint16 length; + if (!data.Copy(offset + 2, length)) { + offset++; + continue; + } + + length = Endian::BigToNative(length); + uint16 segmentLength = length + 2; + + if (offset + segmentLength > data.GetSize()) { + offset++; + continue; + } + + std::string label = "Marker " + std::to_string(segmentCount); + settings.AddZone(offset, segmentLength, colors[colorIndex], label.c_str()); + offset += segmentLength; + colorIndex = (colorIndex + 1) % colors.size(); + segmentCount++; + + if (marker_type == 0xDA) { + if (offset + segmentLength < dataSize - 2) { + settings.AddZone(offset, dataSize - 2 - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); + offset = dataSize - 2; + } + break; + } + } + + if (offset < dataSize - 2) { + settings.AddZone(offset, dataSize - 2 - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); + offset = dataSize - 2; + } + + if (offset < dataSize) { + uint8 byte1, byte2; + if (data.Copy(offset, byte1) && data.Copy(offset + 1, byte2)) { + if ((byte2 << 8 | byte1) == 0xD9FF) { + settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); + } + } + } jpg->selectionZoneInterface = win->GetSelectionZoneInterfaceFromViewerCreation(settings); } From 2a376d287c02e7c1b75391a061559202405f7fbe Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Fri, 26 Jul 2024 13:52:15 +0300 Subject: [PATCH 5/8] updated submodule --- AppCUI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AppCUI b/AppCUI index 6bf1655f..13fdd6e9 160000 --- a/AppCUI +++ b/AppCUI @@ -1 +1 @@ -Subproject commit 6bf1655f8317c4333e568ebd08409c9eb42b23d1 +Subproject commit 13fdd6e9f422e9f1f3e679c051d85d0ca646971b From 27622a961adf5972e401aac4b0d41c46c4d41ba1 Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Fri, 26 Jul 2024 14:18:27 +0300 Subject: [PATCH 6/8] merge conflict fix --- AppCUI | 2 +- Types/JPG/include/jpg.hpp | 6 +-- Types/JPG/src/JPGFile.cpp | 29 +++++------- Types/JPG/src/PanelInformation.cpp | 4 +- Types/JPG/src/jpg.cpp | 76 +++++++++++++++++++++++++++++- vcpkg.json | 6 ++- 6 files changed, 98 insertions(+), 25 deletions(-) diff --git a/AppCUI b/AppCUI index fad200a4..13fdd6e9 160000 --- a/AppCUI +++ b/AppCUI @@ -1 +1 @@ -Subproject commit fad200a496427aceab5598c785fb3c9ff58ea17f +Subproject commit 13fdd6e9f422e9f1f3e679c051d85d0ca646971b diff --git a/Types/JPG/include/jpg.hpp b/Types/JPG/include/jpg.hpp index 0a93476a..d12fa1be 100644 --- a/Types/JPG/include/jpg.hpp +++ b/Types/JPG/include/jpg.hpp @@ -14,6 +14,9 @@ namespace Type 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; struct Header { uint16 soi; // Start of Image marker @@ -32,11 +35,8 @@ namespace Type }; struct SOF0MarkerSegment { - uint16 length; - uint8 precision; uint16 height; uint16 width; - uint8 numberOfComponents; }; #pragma pack(pop) // Back to default packing diff --git a/Types/JPG/src/JPGFile.cpp b/Types/JPG/src/JPGFile.cpp index d86801a3..14aee436 100644 --- a/Types/JPG/src/JPGFile.cpp +++ b/Types/JPG/src/JPGFile.cpp @@ -14,31 +14,26 @@ bool JPGFile::Update() auto& data = this->obj->GetData(); - if (!data.Copy
(0, header)) - return false; - if (!data.Copy(sizeof(Header), app0MarkerSegment)) - return false; + CHECK(data.Copy
(0, header), false, ""); + CHECK(data.Copy(sizeof(Header), app0MarkerSegment), false, ""); uint64 offset = sizeof(Header) + sizeof(App0MarkerSegment); + bool found = false; while (offset < data.GetSize()) { uint16 marker; - if (!data.Copy(offset, marker)) - return false; - if (marker == JPG_SOF0_MARKER) + CHECK(data.Copy(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) { - if (!data.Copy(offset + 2, sof0MarkerSegment)) - return false; + CHECK(data.Copy(offset + 5, sof0MarkerSegment), false, ""); + found = true; break; } - offset += 2; - uint16 segmentLength; - if (!data.Copy(offset, segmentLength)) - return false; - offset += segmentLength; + offset += 1; } - - return true; + return found; } bool JPGFile::LoadImageToObject(Image& img, uint32 index) @@ -50,7 +45,7 @@ bool JPGFile::LoadImageToObject(Image& img, uint32 index) CHECK(buf.IsValid(), false, "Fail to copy Entire file"); bf = (BufferView) buf; } - CHECK(img.Create(buf), false, ""); + CHECK(img.Create(bf), false, ""); return true; } \ No newline at end of file diff --git a/Types/JPG/src/PanelInformation.cpp b/Types/JPG/src/PanelInformation.cpp index 1b940d42..53d09c4f 100644 --- a/Types/JPG/src/PanelInformation.cpp +++ b/Types/JPG/src/PanelInformation.cpp @@ -23,7 +23,9 @@ void Panels::Information::UpdateGeneralInformation() // size general->AddItem({ "Size", tempStr.Format("%s bytes", n.ToString(jpg->obj->GetData().GetSize(), { NumericFormatFlags::None, 10, 3, ',' }).data()) }); // Size - general->AddItem({ "Size", tempStr.Format("%u x %u", jpg->sof0MarkerSegment.width, jpg->sof0MarkerSegment.height) }); + 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) }); diff --git a/Types/JPG/src/jpg.cpp b/Types/JPG/src/jpg.cpp index adf9be84..b14c9e7d 100644 --- a/Types/JPG/src/jpg.cpp +++ b/Types/JPG/src/jpg.cpp @@ -34,8 +34,80 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str { 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"); + const std::vector colors = { ColorPair{ Color::Teal, Color::DarkBlue }, ColorPair{ Color::Yellow, Color::DarkBlue } }; + + auto& data = jpg->obj->GetData(); + const uint64 dataSize = data.GetSize(); + uint64 offset = 0; + uint32 colorIndex = 0; + uint32 segmentCount = 1; + + settings.AddZone(0, sizeof(JPG::Header), ColorPair{ Color::Magenta, Color::DarkBlue }, "SOI Segment"); + offset += sizeof(JPG::Header); + + settings.AddZone(offset, sizeof(JPG::App0MarkerSegment), ColorPair{ Color::Olive, Color::DarkBlue }, "APP0 Segment"); + offset += sizeof(JPG::App0MarkerSegment); + + while (offset < dataSize - 2) { + uint8 marker_prefix; + uint8 marker_type; + + if (!data.Copy(offset, marker_prefix) || marker_prefix != 0xFF) { + break; + } + + if (!data.Copy(offset + 1, marker_type) || marker_type == 0x00 || marker_type == 0xFF) { + offset++; + continue; + } + + if (marker_type == 0xD9) { + settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); + break; + } + + uint16 length; + if (!data.Copy(offset + 2, length)) { + offset++; + continue; + } + + length = Endian::BigToNative(length); + uint16 segmentLength = length + 2; + + if (offset + segmentLength > data.GetSize()) { + offset++; + continue; + } + + std::string label = "Marker " + std::to_string(segmentCount); + settings.AddZone(offset, segmentLength, colors[colorIndex], label.c_str()); + offset += segmentLength; + colorIndex = (colorIndex + 1) % colors.size(); + segmentCount++; + + if (marker_type == 0xDA) { + if (offset + segmentLength < dataSize - 2) { + settings.AddZone(offset, dataSize - 2 - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); + offset = dataSize - 2; + } + break; + } + } + + if (offset < dataSize - 2) { + settings.AddZone(offset, dataSize - 2 - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); + offset = dataSize - 2; + } + + if (offset < dataSize) { + uint8 byte1, byte2; + if (data.Copy(offset, byte1) && data.Copy(offset + 1, byte2)) { + if ((byte2 << 8 | byte1) == 0xD9FF) { + settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); + } + } + } jpg->selectionZoneInterface = win->GetSelectionZoneInterfaceFromViewerCreation(settings); } diff --git a/vcpkg.json b/vcpkg.json index cfce5468..31fa3d90 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -63,7 +63,11 @@ { "name": "re2", "platform": "windows | linux | osx" - } + }, + { + "name": "libjpeg-turbo", + "platform": "windows | linux | osx" + } ], "vcpkg-configuration": { "overlay-ports": [ "./AppCUI/vcpkg-ports/ncurses"] From 9836bee9b5c7c34d145bb921588bbb52d54b9521 Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Fri, 26 Jul 2024 15:09:55 +0300 Subject: [PATCH 7/8] added extra info for panel, small fixes --- Types/JPG/include/jpg.hpp | 4 ++++ Types/JPG/src/PanelInformation.cpp | 14 +++++++++----- Types/JPG/src/jpg.cpp | 20 +++++++++++--------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Types/JPG/include/jpg.hpp b/Types/JPG/include/jpg.hpp index d12fa1be..c99befd8 100644 --- a/Types/JPG/include/jpg.hpp +++ b/Types/JPG/include/jpg.hpp @@ -17,6 +17,10 @@ namespace Type 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 diff --git a/Types/JPG/src/PanelInformation.cpp b/Types/JPG/src/PanelInformation.cpp index 53d09c4f..4d7c6284 100644 --- a/Types/JPG/src/PanelInformation.cpp +++ b/Types/JPG/src/PanelInformation.cpp @@ -28,12 +28,15 @@ void Panels::Information::UpdateGeneralInformation() 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({ "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) }); - */ } @@ -47,8 +50,9 @@ void Panels::Information::RecomputePanelsPositions() int w = this->GetWidth(); int h = this->GetHeight(); - if ((!general.IsValid()) || (!issues.IsValid())) + if ((!general.IsValid()) || (!issues.IsValid())){ return; + } issues->SetVisible(false); this->general->Resize(w, h); diff --git a/Types/JPG/src/jpg.cpp b/Types/JPG/src/jpg.cpp index b14c9e7d..67beedc5 100644 --- a/Types/JPG/src/jpg.cpp +++ b/Types/JPG/src/jpg.cpp @@ -13,15 +13,17 @@ 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)) + if (buf.GetLength() < sizeof(JPG::Header) + sizeof(JPG::App0MarkerSegment)) { return false; + } auto header = buf.GetObject(); - if (header->soi != JPG::JPG_SOI_MARKER || header->app0 != JPG::JPG_APP0_MARKER) + if (header->soi != JPG::JPG_SOI_MARKER || header->app0 != JPG::JPG_APP0_MARKER) { return false; + } auto app0MarkerSegment = buf.GetObject(sizeof(JPG::Header)); - if (memcmp(app0MarkerSegment->identifier, "JFIF", 5) != 0) + if (memcmp(app0MarkerSegment->identifier, "JFIF", 5) != 0) { return false; - // all good + } return true; } @@ -52,16 +54,16 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str uint8 marker_prefix; uint8 marker_type; - if (!data.Copy(offset, marker_prefix) || marker_prefix != 0xFF) { + if (!data.Copy(offset, marker_prefix) || marker_prefix != JPG::JPG_START_MAKER_BYTE) { break; } - if (!data.Copy(offset + 1, marker_type) || marker_type == 0x00 || marker_type == 0xFF) { + if (!data.Copy(offset + 1, marker_type) || marker_type == 0x00 || marker_type == JPG::JPG_START_MAKER_BYTE) { offset++; continue; } - if (marker_type == 0xD9) { + if (marker_type == JPG::JPG_EOI_BYTE) { settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); break; } @@ -86,7 +88,7 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str colorIndex = (colorIndex + 1) % colors.size(); segmentCount++; - if (marker_type == 0xDA) { + if (marker_type == JPG::JPG_SOS_BYTE) { if (offset + segmentLength < dataSize - 2) { settings.AddZone(offset, dataSize - 2 - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); offset = dataSize - 2; @@ -103,7 +105,7 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str if (offset < dataSize) { uint8 byte1, byte2; if (data.Copy(offset, byte1) && data.Copy(offset + 1, byte2)) { - if ((byte2 << 8 | byte1) == 0xD9FF) { + if ((byte2 << 8 | byte1) == JPG::JPG_EOI_MARKER) { settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); } } From 9766af32f7a27675d7dc311ad7ee76a0db06fc86 Mon Sep 17 00:00:00 2001 From: Rusu Andrei Date: Fri, 26 Jul 2024 18:13:01 +0300 Subject: [PATCH 8/8] added MARKER_SIZE for bufferview --- Types/JPG/include/jpg.hpp | 1 + Types/JPG/src/jpg.cpp | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Types/JPG/include/jpg.hpp b/Types/JPG/include/jpg.hpp index c99befd8..7a155377 100644 --- a/Types/JPG/include/jpg.hpp +++ b/Types/JPG/include/jpg.hpp @@ -20,6 +20,7 @@ namespace Type constexpr uint8 JPG_START_MAKER_BYTE = 0xFF; constexpr uint8 JPG_SOS_BYTE = 0xDA; constexpr uint8 JPG_EOI_BYTE = 0xD9; + constexpr uint8 MARKER_SIZE = 2; struct Header { diff --git a/Types/JPG/src/jpg.cpp b/Types/JPG/src/jpg.cpp index 67beedc5..74f048a6 100644 --- a/Types/JPG/src/jpg.cpp +++ b/Types/JPG/src/jpg.cpp @@ -50,7 +50,7 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str settings.AddZone(offset, sizeof(JPG::App0MarkerSegment), ColorPair{ Color::Olive, Color::DarkBlue }, "APP0 Segment"); offset += sizeof(JPG::App0MarkerSegment); - while (offset < dataSize - 2) { + while (offset < dataSize - JPG::MARKER_SIZE) { uint8 marker_prefix; uint8 marker_type; @@ -64,18 +64,18 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str } if (marker_type == JPG::JPG_EOI_BYTE) { - settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); + settings.AddZone(offset, JPG::MARKER_SIZE, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); break; } uint16 length; - if (!data.Copy(offset + 2, length)) { + if (!data.Copy(offset + JPG::MARKER_SIZE, length)) { offset++; continue; } length = Endian::BigToNative(length); - uint16 segmentLength = length + 2; + uint16 segmentLength = length + JPG::MARKER_SIZE; if (offset + segmentLength > data.GetSize()) { offset++; @@ -89,24 +89,24 @@ PLUGIN_EXPORT bool Validate(const AppCUI::Utils::BufferView& buf, const std::str segmentCount++; if (marker_type == JPG::JPG_SOS_BYTE) { - if (offset + segmentLength < dataSize - 2) { - settings.AddZone(offset, dataSize - 2 - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); - offset = dataSize - 2; + if (offset + segmentLength < dataSize - JPG::MARKER_SIZE) { + settings.AddZone(offset, dataSize - JPG::MARKER_SIZE - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); + offset = dataSize - JPG::MARKER_SIZE; } break; } } - if (offset < dataSize - 2) { - settings.AddZone(offset, dataSize - 2 - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); - offset = dataSize - 2; + if (offset < dataSize - JPG::MARKER_SIZE) { + settings.AddZone(offset, dataSize - JPG::MARKER_SIZE - offset, ColorPair{ Color::Red, Color::DarkBlue }, "Compressed Data"); + offset = dataSize - JPG::MARKER_SIZE; } if (offset < dataSize) { uint8 byte1, byte2; if (data.Copy(offset, byte1) && data.Copy(offset + 1, byte2)) { if ((byte2 << 8 | byte1) == JPG::JPG_EOI_MARKER) { - settings.AddZone(offset, 2, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); + settings.AddZone(offset, JPG::MARKER_SIZE, ColorPair{ Color::Magenta, Color::DarkBlue }, "EOI Segment"); } } }