Repository navigation
Vips: preserve bit depth of high-bit-depth AVIF in sub-sizes #79556
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,16 +6,26 @@ import type { ImageSizeCrop } from '../types'; | |
|
|
||
| const mockThumbnailBuffer = jest.fn( () => new MockImage() ); | ||
| const mockCrop = jest.fn( () => new MockImage() ); | ||
| const mockResize = jest.fn( () => new MockImage() ); | ||
| const mockNewFromBuffer = jest.fn( () => new MockImage() ); | ||
| const mockWriteToBuffer = jest.fn( () => ( { | ||
| buffer: '', | ||
| } ) ); | ||
|
|
||
| // Controls the `heif-bitdepth` value reported by the mocked source image so | ||
| // tests can exercise both the standard (8-bit) and high-bit-depth code paths. | ||
| let mockBitdepth = 8; | ||
|
|
||
| class MockImage { | ||
| width = 100; | ||
| height = 100; | ||
| pageHeight = 100; | ||
| crop = mockCrop; | ||
| writeToBuffer = jest.fn( () => ( { | ||
| buffer: '', | ||
| } ) ); | ||
| resize = mockResize; | ||
| writeToBuffer = mockWriteToBuffer; | ||
| getInt = jest.fn( ( name: string ) => | ||
| 'heif-bitdepth' === name ? mockBitdepth : 0 | ||
| ); | ||
| } | ||
|
|
||
| class MockVipsImage { | ||
|
|
@@ -35,6 +45,7 @@ jest.mock( 'wasm-vips', () => | |
| describe( 'resizeImage', () => { | ||
| afterEach( () => { | ||
| jest.clearAllMocks(); | ||
| mockBitdepth = 8; | ||
| } ); | ||
|
|
||
| it( 'resizes without crop', async () => { | ||
|
|
@@ -223,4 +234,71 @@ describe( 'resizeImage', () => { | |
|
|
||
| expect( mockCrop ).toHaveBeenCalledWith( ...expected ); | ||
| } ); | ||
|
|
||
| describe( 'high-bit-depth AVIF', () => { | ||
| it( 'preserves bit depth when resizing a 10-bit AVIF without crop', async () => { | ||
| mockBitdepth = 10; | ||
| const avifFile = new File( [ '<BLOB>' ], 'example.avif', { | ||
| type: 'image/avif', | ||
| } ); | ||
| const buffer = await avifFile.arrayBuffer(); | ||
|
|
||
| await resizeImage( 'itemId', buffer, 'image/avif', { | ||
| width: 50, | ||
| height: 50, | ||
| } ); | ||
|
|
||
| // Uses the precision-preserving resize path, not `thumbnail` | ||
| // (which would flatten the samples to 8-bit sRGB). | ||
| expect( mockResize ).toHaveBeenCalled(); | ||
| expect( mockThumbnailBuffer ).not.toHaveBeenCalled(); | ||
| // Writes the source bit depth so the sub-size stays 10-bit. | ||
| expect( mockWriteToBuffer ).toHaveBeenCalledWith( | ||
| '.avif', | ||
| expect.objectContaining( { bitdepth: 10 } ) | ||
| ); | ||
| } ); | ||
|
|
||
| it( 'centre-crops a 12-bit AVIF while preserving bit depth', async () => { | ||
| mockBitdepth = 12; | ||
| const avifFile = new File( [ '<BLOB>' ], 'example.avif', { | ||
| type: 'image/avif', | ||
| } ); | ||
| const buffer = await avifFile.arrayBuffer(); | ||
|
|
||
| await resizeImage( 'itemId', buffer, 'image/avif', { | ||
| width: 50, | ||
| height: 50, | ||
| crop: true, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be worth adding a test case to cover crop being passed an array of
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mainly we are testing color depth after cropping, I wouldn't expect changing the cropping to impact the outcome, but we can add at least one cropped variant in the tests to make sure. |
||
| } ); | ||
|
|
||
| expect( mockResize ).toHaveBeenCalled(); | ||
| expect( mockCrop ).toHaveBeenCalled(); | ||
| expect( mockThumbnailBuffer ).not.toHaveBeenCalled(); | ||
| expect( mockWriteToBuffer ).toHaveBeenCalledWith( | ||
| '.avif', | ||
| expect.objectContaining( { bitdepth: 12 } ) | ||
| ); | ||
| } ); | ||
|
|
||
| it( 'uses the standard thumbnail path for an 8-bit AVIF', async () => { | ||
| mockBitdepth = 8; | ||
| const avifFile = new File( [ '<BLOB>' ], 'example.avif', { | ||
| type: 'image/avif', | ||
| } ); | ||
| const buffer = await avifFile.arrayBuffer(); | ||
|
|
||
| await resizeImage( 'itemId', buffer, 'image/avif', { | ||
| width: 50, | ||
| height: 50, | ||
| } ); | ||
|
|
||
| expect( mockThumbnailBuffer ).toHaveBeenCalled(); | ||
| expect( mockResize ).not.toHaveBeenCalled(); | ||
| expect( mockWriteToBuffer ).toHaveBeenCalledWith( | ||
| '.avif', | ||
| expect.not.objectContaining( { bitdepth: expect.anything() } ) | ||
| ); | ||
| } ); | ||
| } ); | ||
| } ); | ||
Uh oh!
There was an error while loading. Please reload this page.