From 23eb43c691c9d3debd0f49c51b2bcd76e3b932d6 Mon Sep 17 00:00:00 2001 From: Antoine Aflalo <197810+Belphemur@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:03:35 -0500 Subject: [PATCH] fix(chapter): fix chapter conversion. Still need to figure out the memory issues Consolidates image conversion logic into a dedicated method. This change streamlines the conversion process by centralizing the setting of converted image data, extension, and size. It also introduces a flag to track whether an image has been converted. The old resource cleanup has been removed since it is not needed anymore. --- .idea/inspectionProfiles/Project_Default.xml | 10 ++ .vscode/launch.json | 16 +++ internal/manga/page_container.go | 21 ++-- pkg/converter/webp/webp_converter.go | 5 +- pkg/converter/webp/webp_converter_test.go | 104 +++++++++++++++---- 5 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .vscode/launch.json diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..99d96b1 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..81d924e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}" + } + ] +} \ No newline at end of file diff --git a/internal/manga/page_container.go b/internal/manga/page_container.go index 466d8d1..f125efc 100644 --- a/internal/manga/page_container.go +++ b/internal/manga/page_container.go @@ -1,6 +1,9 @@ package manga -import "image" +import ( + "bytes" + "image" +) // PageContainer is a struct that holds a manga page, its image, and the image format. type PageContainer struct { @@ -12,16 +15,18 @@ type PageContainer struct { Format string // IsToBeConverted is a boolean flag indicating whether the image needs to be converted to another format. IsToBeConverted bool + // HasBeenConverted is a boolean flag indicating whether the image has been converted to another format. + HasBeenConverted bool } func NewContainer(Page *Page, img image.Image, format string, isToBeConverted bool) *PageContainer { - return &PageContainer{Page: Page, Image: img, Format: format, IsToBeConverted: isToBeConverted} + return &PageContainer{Page: Page, Image: img, Format: format, IsToBeConverted: isToBeConverted, HasBeenConverted: false} } -// Close releases resources held by the PageContainer -func (pc *PageContainer) Close() { - pc.Image = nil - if pc.Page != nil && pc.Page.Contents != nil { - pc.Page.Contents.Reset() - } +// SetConverted sets the converted image, its extension, and its size in the PageContainer. +func (pc *PageContainer) SetConverted(converted *bytes.Buffer, extension string) { + pc.Page.Contents = converted + pc.Page.Extension = extension + pc.Page.Size = uint64(converted.Len()) + pc.HasBeenConverted = true } diff --git a/pkg/converter/webp/webp_converter.go b/pkg/converter/webp/webp_converter.go index 683cc59..4d2aaa8 100644 --- a/pkg/converter/webp/webp_converter.go +++ b/pkg/converter/webp/webp_converter.go @@ -80,7 +80,6 @@ func (converter *Converter) ConvertChapter(chapter *manga.Chapter, quality uint8 go func(pageToConvert *manga.PageContainer) { defer func() { wgConvertedPages.Done() - pageToConvert.Close() // Clean up resources <-guard }() @@ -243,9 +242,7 @@ func (converter *Converter) convertPage(container *manga.PageContainer, quality if err != nil { return nil, err } - container.Page.Contents = converted - container.Page.Extension = ".webp" - container.Page.Size = uint64(converted.Len()) + container.SetConverted(converted, ".webp"); return container, nil } diff --git a/pkg/converter/webp/webp_converter_test.go b/pkg/converter/webp/webp_converter_test.go index 7f2e9d5..1566315 100644 --- a/pkg/converter/webp/webp_converter_test.go +++ b/pkg/converter/webp/webp_converter_test.go @@ -4,37 +4,96 @@ import ( "bytes" "image" "image/color" - "image/draw" + "image/jpeg" "image/png" "sync" "testing" + _ "image/jpeg" + + _ "golang.org/x/image/webp" + "github.com/belphemur/CBZOptimizer/v2/internal/manga" "github.com/belphemur/CBZOptimizer/v2/pkg/converter/constant" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func createTestImage(width, height int) image.Image { +func createTestImage(width, height int, format string) (image.Image, error) { img := image.NewRGBA(image.Rect(0, 0, width, height)) - draw.Draw(img, img.Bounds(), &image.Uniform{color.White}, image.Point{}, draw.Src) - return img + + // Create a gradient pattern to ensure we have actual image data + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + img.Set(x, y, color.RGBA{ + R: uint8((x * 255) / width), + G: uint8((y * 255) / height), + B: 100, + A: 255, + }) + } + } + return img, nil } -func createTestPage(t *testing.T, index int, width, height int) *manga.Page { - img := createTestImage(width, height) - var buf bytes.Buffer - err := png.Encode(&buf, img) +func encodeImage(img image.Image, format string) (*bytes.Buffer, string, error) { + buf := new(bytes.Buffer) + + switch format { + case "jpeg", "jpg": + if err := jpeg.Encode(buf, img, &jpeg.Options{Quality: 85}); err != nil { + return nil, "", err + } + return buf, ".jpg", nil + case "webp": + PrepareEncoder() + if err := Encode(buf, img, 80); err != nil { + return nil, "", err + } + return buf, ".webp", nil + case "png": + fallthrough + default: + if err := png.Encode(buf, img); err != nil { + return nil, "", err + } + return buf, ".png", nil + } +} + +func createTestPage(t *testing.T, index int, width, height int, format string) *manga.Page { + img, err := createTestImage(width, height, format) + require.NoError(t, err) + + buf, ext, err := encodeImage(img, format) require.NoError(t, err) return &manga.Page{ Index: uint16(index), - Contents: &buf, - Extension: ".png", + Contents: buf, + Extension: ext, Size: uint64(buf.Len()), } } +func validateConvertedImage(t *testing.T, page *manga.Page) { + require.NotNil(t, page.Contents) + require.Greater(t, page.Contents.Len(), 0) + + // Try to decode the image + img, format, err := image.Decode(bytes.NewReader(page.Contents.Bytes())) + require.NoError(t, err, "Failed to decode converted image") + + if page.Extension == ".webp" { + assert.Equal(t, "webp", format, "Expected WebP format") + } + + require.NotNil(t, img) + bounds := img.Bounds() + assert.Greater(t, bounds.Dx(), 0, "Image width should be positive") + assert.Greater(t, bounds.Dy(), 0, "Image height should be positive") +} + // TestConverter_ConvertChapter tests the ConvertChapter method of the WebP converter. // It verifies various scenarios including: // - Converting single normal images @@ -63,7 +122,7 @@ func TestConverter_ConvertChapter(t *testing.T) { }{ { name: "Single normal image", - pages: []*manga.Page{createTestPage(t, 1, 800, 1200)}, + pages: []*manga.Page{createTestPage(t, 1, 800, 1200, "jpeg")}, split: false, expectSplit: false, numExpected: 1, @@ -71,8 +130,8 @@ func TestConverter_ConvertChapter(t *testing.T) { { name: "Multiple normal images", pages: []*manga.Page{ - createTestPage(t, 1, 800, 1200), - createTestPage(t, 2, 800, 1200), + createTestPage(t, 1, 800, 1200, "png"), + createTestPage(t, 2, 800, 1200, "jpeg"), }, split: false, expectSplit: false, @@ -80,14 +139,14 @@ func TestConverter_ConvertChapter(t *testing.T) { }, { name: "Tall image with split enabled", - pages: []*manga.Page{createTestPage(t, 1, 800, 5000)}, + pages: []*manga.Page{createTestPage(t, 1, 800, 5000, "jpeg")}, split: true, expectSplit: true, numExpected: 3, // Based on cropHeight of 2000 }, { name: "Tall image without split", - pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100)}, + pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100, "png")}, split: false, expectError: true, numExpected: 1, @@ -128,6 +187,11 @@ func TestConverter_ConvertChapter(t *testing.T) { require.NotNil(t, convertedChapter) assert.Len(t, convertedChapter.Pages, tt.numExpected) + // Validate all converted images + for _, page := range convertedChapter.Pages { + validateConvertedImage(t, page) + } + // Verify page order for i := 1; i < len(convertedChapter.Pages); i++ { prevPage := convertedChapter.Pages[i-1] @@ -189,9 +253,10 @@ func TestConverter_convertPage(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - page := createTestPage(t, 1, 100, 100) - container := manga.NewContainer(page, createTestImage(100, 100), tt.format, tt.isToBeConverted) - defer container.Close() + page := createTestPage(t, 1, 100, 100, tt.format) + img, err := createTestImage(100, 100, tt.format) + require.NoError(t, err) + container := manga.NewContainer(page, img, tt.format, tt.isToBeConverted) converted, err := converter.convertPage(container, 80) require.NoError(t, err) @@ -199,6 +264,7 @@ func TestConverter_convertPage(t *testing.T) { if tt.expectWebP { assert.Equal(t, ".webp", converted.Page.Extension) + validateConvertedImage(t, converted.Page) } else { assert.NotEqual(t, ".webp", converted.Page.Extension) } @@ -238,7 +304,7 @@ func TestConverter_checkPageNeedsSplit(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - page := createTestPage(t, 1, 800, tt.imageHeight) + page := createTestPage(t, 1, 800, tt.imageHeight, "jpeg") needsSplit, img, format, err := converter.checkPageNeedsSplit(page, tt.split)