TGA: Support for TGA specification 2.0
Adds TGA 2.0 compliance: - Support for Extension Area, Developer Area and Footer (metadata support) - Support for 15-bit and 16-bit per pixel images (both RGB and Indexed) - Full support for rotation on reading (we cannot use Qt transformations because only a subset is part of the TGA specification) - When writing you can choose the supported version (subType) - Improved writing speed (approximately 10 times) and removed whole image conversions (significant memory savings) It pass the [TrueVision TGA 2.0 conformance suite](https://github.com/zigimg/test-suite/tree/master/fixtures/tga). Test changes: - Read test: added ability to skip a specific test on sequential devices (via JSON behavior file) - Write test: added the ability to set the subType when writing (via JSON properties file) Closes #37
@ -189,7 +189,7 @@ kimageformats_write_tests(
|
||||
pic-lossless
|
||||
qoi-lossless
|
||||
rgb-lossless
|
||||
tga # fixme: the alpha images appear not to be written properly
|
||||
tga-nodatacheck
|
||||
)
|
||||
|
||||
# EPS read tests depend on the vagaries of GhostScript
|
||||
|
@ -88,30 +88,33 @@ are iterated sequentially and the first object that matches the requirements
|
||||
is used for testing (successes are ignored).
|
||||
|
||||
Supported values for JSON objects:
|
||||
- `comment`: Type string. A string shown by the test when a condition occurs.
|
||||
- `description`: Type string. A description of the object. Not used by the
|
||||
- `comment`: Type `string`. A string shown by the test when a condition occurs.
|
||||
- `description`: Type `string`. A description of the object. Not used by the
|
||||
test.
|
||||
- `disableAutoTransform`: Type boolean. By default, tests are run with
|
||||
- `disableAutoTransform`: Type `boolean`. By default, tests are run with
|
||||
autotransform enabled (i.e. rotation is applied if the plugin supports it).
|
||||
Set to `true` to disable autotransform.
|
||||
- `filename`: Type string. Name of the template file to use. E.g.
|
||||
- `filename`: Type `string`. Name of the template file to use. E.g.
|
||||
"testRGB_Qt_6_2.png".
|
||||
- `fuzziness`: Type integer. Set the fuzziness only if not already set on the
|
||||
- `fuzziness`: Type `integer`. Set the fuzziness only if not already set on the
|
||||
command line. The value set on the command line wins over the one in the JSON
|
||||
file.
|
||||
- `maxQtVersion`: Type string. Maximum Qt version this object is compatible
|
||||
- `maxQtVersion`: Type `string`. Maximum Qt version this object is compatible
|
||||
with (if not set means all). E.g. "6.2.99".
|
||||
- `metadata`: Type Array. An array of key/value objects (string type)
|
||||
- `metadata`: Type `array`. An array of key/value objects (string type)
|
||||
containing the image metadata as returned by `QImage::text`.
|
||||
- `minQtVersion`: Type string. Minimum Qt version this object is compatible
|
||||
- `minQtVersion`: Type `string`. Minimum Qt version this object is compatible
|
||||
with (if not set means all). E.g. "6.2.0".
|
||||
- `perceptiveFuzziness` Type boolean. Set the perceptive fuzziness only if not
|
||||
- `perceptiveFuzziness` Type `boolean`. Set the perceptive fuzziness only if not
|
||||
already set on the command line. The value set on the command line wins over
|
||||
the one in the JSON file.
|
||||
- `resolution`: Type object. An object with the `dotsPerMeterX` and
|
||||
- `resolution`: Type `object`. An object with the `dotsPerMeterX` and
|
||||
`dotsPerMeterY` (integer) values of the image.
|
||||
- `seeAlso`: Type string. More info about the object. Normally used to point
|
||||
- `seeAlso`: Type `string`. More info about the object. Normally used to point
|
||||
to bug reports. Not used by the test.
|
||||
- `skipSequential`: Type `boolean`. Skip the test on sequential access device.
|
||||
Some plugins may have limited functionality on sequential devices (e.g.,
|
||||
not reading metadata).
|
||||
- `unsupportedFormat`: Type `boolean`. When true, the test is skipped.
|
||||
|
||||
Some examples:
|
||||
@ -169,11 +172,12 @@ See also [Add a test to CMakeLists.txt](#add-a-test-to-cmakeliststxt).
|
||||
The properties file must be located in `write/basic` and must have the name
|
||||
of the file format (e.g. jxl.json). It is a JSON object composed of the
|
||||
following values:
|
||||
- `format`: Type string. The format tested.
|
||||
- `metadata`: Type Array. An array of key/value objects (string type)
|
||||
- `format`: Type `string`. The format tested.
|
||||
- `metadata`: Type `array`. An array of key/value objects (string type)
|
||||
containing the image metadata as returned by `QImage::text`.
|
||||
- `resolution`: Type object. An object with the `dotsPerMeterX` and `
|
||||
- `resolution`: Type `object`. An object with the `dotsPerMeterX` and `
|
||||
dotsPerMeterY` (integer) values of the image.
|
||||
- `subType`: type `string`. The image writer subtype to set when testing.
|
||||
|
||||
[This is an example](write/basic/jxl.json) of property file.
|
||||
|
||||
|
BIN
autotests/read/tga/bottom_left.tga
Normal file
After Width: | Height: | Size: 226 KiB |
5
autotests/read/tga/bottom_left.tga.json
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation.png"
|
||||
}
|
||||
]
|
BIN
autotests/read/tga/bottom_right.tga
Normal file
After Width: | Height: | Size: 226 KiB |
5
autotests/read/tga/bottom_right.tga.json
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation.png"
|
||||
}
|
||||
]
|
BIN
autotests/read/tga/devarea.tga
Normal file
After Width: | Height: | Size: 80 KiB |
56
autotests/read/tga/devarea.tga.json
Normal file
@ -0,0 +1,56 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "extarea.png",
|
||||
"skipSequential" : true,
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Author",
|
||||
"value" : "KDE Project"
|
||||
},
|
||||
{
|
||||
"key" : "Comment",
|
||||
"value" : "TV broadcast test image."
|
||||
},
|
||||
{
|
||||
"key" : "Altitude",
|
||||
"value" : "34"
|
||||
},
|
||||
{
|
||||
"key" : "Copyright",
|
||||
"value" : "@2025 KDE Project"
|
||||
},
|
||||
{
|
||||
"key" : "Latitude",
|
||||
"value" : "44.6478"
|
||||
},
|
||||
{
|
||||
"key" : "LensManufacturer",
|
||||
"value" : "KDE Glasses"
|
||||
},
|
||||
{
|
||||
"key" : "LensModel",
|
||||
"value" : "A1234"
|
||||
},
|
||||
{
|
||||
"key" : "Longitude",
|
||||
"value" : "10.9254"
|
||||
},
|
||||
{
|
||||
"key" : "Manufacturer",
|
||||
"value" : "KFramework"
|
||||
},
|
||||
{
|
||||
"key" : "Model",
|
||||
"value" : "KImageFormats"
|
||||
},
|
||||
{
|
||||
"key" : "Software",
|
||||
"value" : "LIFE Pro 2.18.30 (Linux)"
|
||||
}
|
||||
],
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 11811,
|
||||
"dotsPerMeterY" : 5906
|
||||
}
|
||||
}
|
||||
]
|
BIN
autotests/read/tga/extarea.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
autotests/read/tga/extarea.tga
Normal file
32
autotests/read/tga/extarea.tga.json
Normal file
@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "extarea.png",
|
||||
"skipSequential" : true,
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Title",
|
||||
"value" : "Test Card"
|
||||
},
|
||||
{
|
||||
"key" : "Author",
|
||||
"value" : "KDE Project"
|
||||
},
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
"value" : "2025-08-21T07:32:45"
|
||||
},
|
||||
{
|
||||
"key" : "Comment",
|
||||
"value" : "TV broadcast test image."
|
||||
},
|
||||
{
|
||||
"key" : "Software",
|
||||
"value" : "LIFE Pro 2.18.31 (Linux)"
|
||||
}
|
||||
],
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 3937,
|
||||
"dotsPerMeterY" : 3937
|
||||
}
|
||||
}
|
||||
]
|
BIN
autotests/read/tga/orientation.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
autotests/read/tga/rgb16.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
autotests/read/tga/rgb16.tga
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
autotests/read/tga/top_left.tga
Normal file
After Width: | Height: | Size: 226 KiB |
5
autotests/read/tga/top_left.tga.json
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation.png"
|
||||
}
|
||||
]
|
BIN
autotests/read/tga/top_right.tga
Normal file
After Width: | Height: | Size: 226 KiB |
5
autotests/read/tga/top_right.tga.json
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation.png"
|
||||
}
|
||||
]
|
@ -281,6 +281,12 @@ int main(int argc, char **argv)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seq && timg.skipSequentialDeviceTest()) {
|
||||
QTextStream(stdout) << "SKIP : " << fi.fileName() << ": marked to be skipped on a sequential device (don't worry, it's ok)\n";
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
|
||||
TemplateImage::TestFlags flags = TemplateImage::None;
|
||||
QString comment;
|
||||
QFileInfo expFileInfo = timg.compareImage(flags, comment);
|
||||
|
@ -76,6 +76,15 @@ bool TemplateImage::isLicense() const
|
||||
return !m_fi.suffix().compare(QStringLiteral("license"), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool TemplateImage::skipSequentialDeviceTest() const
|
||||
{
|
||||
auto obj = searchObject(m_fi);
|
||||
if (obj.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return obj.value("skipSequential").toBool();
|
||||
}
|
||||
|
||||
QFileInfo TemplateImage::compareImage(TestFlags &flags, QString& comment) const
|
||||
{
|
||||
auto fi = jsonImage(flags, comment);
|
||||
|
@ -54,6 +54,12 @@ public:
|
||||
*/
|
||||
bool isLicense() const;
|
||||
|
||||
/*!
|
||||
* \brief skipSequentialDeviceTest
|
||||
* \return tre it the sequential test should be skipped.
|
||||
*/
|
||||
bool skipSequentialDeviceTest() const;
|
||||
|
||||
/*!
|
||||
* \brief compareImage
|
||||
* \param flags Flags for modifying test behavior (e.g. image format not supported by current Qt version).
|
||||
|
62
autotests/write/basic/tga.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"format" : "tga",
|
||||
"subType" : "TGAv2E",
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "CreationDate",
|
||||
"value" : "2025-01-14T13:53:32+01:00"
|
||||
},
|
||||
{
|
||||
"key" : "Direction",
|
||||
"value" : "123.7"
|
||||
},
|
||||
{
|
||||
"key" : "Software" ,
|
||||
"value" : "Adobe Photoshop 26.2 (Windows)"
|
||||
},
|
||||
{
|
||||
"key" : "Altitude",
|
||||
"value" : "34"
|
||||
},
|
||||
{
|
||||
"key" : "Author",
|
||||
"value" : "KDE Project"
|
||||
},
|
||||
{
|
||||
"key" : "Copyright",
|
||||
"value" : "@2025 KDE Project"
|
||||
},
|
||||
{
|
||||
"key" : "Description",
|
||||
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
|
||||
},
|
||||
{
|
||||
"key" : "Latitude",
|
||||
"value" : "44.6478"
|
||||
},
|
||||
{
|
||||
"key" : "LensManufacturer",
|
||||
"value" : "KDE Glasses"
|
||||
},
|
||||
{
|
||||
"key" : "LensModel",
|
||||
"value" : "A1234"
|
||||
},
|
||||
{
|
||||
"key" : "Longitude",
|
||||
"value" : "10.9254"
|
||||
},
|
||||
{
|
||||
"key" : "Manufacturer",
|
||||
"value" : "KFramework"
|
||||
},
|
||||
{
|
||||
"key" : "Model",
|
||||
"value" : "KImageFormats"
|
||||
}
|
||||
],
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 11811,
|
||||
"dotsPerMeterY" : 11812
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
@ -70,6 +70,15 @@ void setOptionalInfo(QImage &image, const QString &suffix)
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray readSubType(const QString &suffix)
|
||||
{
|
||||
auto obj = readOptionalInfo(suffix);
|
||||
if (obj.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
return obj.value("subType").toString().toLatin1();
|
||||
}
|
||||
|
||||
bool checkOptionalInfo(QImage &image, const QString &suffix)
|
||||
{
|
||||
auto obj = readOptionalInfo(suffix);
|
||||
@ -157,6 +166,9 @@ int basicTest(const QString &suffix, bool lossless, bool ignoreDataCheck, bool s
|
||||
{
|
||||
QBuffer buffer(&writtenData);
|
||||
QImageWriter imgWriter(&buffer, format.constData());
|
||||
auto subType = readSubType(suffix);
|
||||
if (!subType.isEmpty())
|
||||
imgWriter.setSubType(subType);
|
||||
if (lossless) {
|
||||
imgWriter.setQuality(100);
|
||||
}
|
||||
|