mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-06-03 17:08:08 -04:00
- 1-bit writer: checks where is black and use NOT operator only if needed - Fix images with witdh == 65536(*) - Checks result of disk writes and reads on all formats (*) PCX formats support images with with of 65536 but only if the header field bytesPerLine is valid (no overflow). This means that the width 65536 is supported on 1bpp images only. The previous version of the plugins wrote an image with width of 65536px in the wrong way and it was unable to read it (wrong image returned). I verified that Photoshop and Gimp weren't able to read the image either.
795 lines
18 KiB
C++
795 lines
18 KiB
C++
/*
|
|
This file is part of the KDE project
|
|
SPDX-FileCopyrightText: 2002-2005 Nadeem Hasan <nhasan@kde.org>
|
|
|
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
|
*/
|
|
|
|
#include "pcx_p.h"
|
|
#include "util_p.h"
|
|
|
|
#include <QColor>
|
|
#include <QDataStream>
|
|
#include <QDebug>
|
|
#include <QImage>
|
|
|
|
#pragma pack(push, 1)
|
|
class RGB
|
|
{
|
|
public:
|
|
quint8 r;
|
|
quint8 g;
|
|
quint8 b;
|
|
|
|
static RGB from(const QRgb color)
|
|
{
|
|
RGB c;
|
|
c.r = qRed(color);
|
|
c.g = qGreen(color);
|
|
c.b = qBlue(color);
|
|
return c;
|
|
}
|
|
};
|
|
|
|
class Palette
|
|
{
|
|
public:
|
|
void setColor(int i, const QRgb color)
|
|
{
|
|
RGB &c = rgb[i];
|
|
c.r = qRed(color);
|
|
c.g = qGreen(color);
|
|
c.b = qBlue(color);
|
|
}
|
|
|
|
QRgb color(int i) const
|
|
{
|
|
return qRgb(rgb[i].r, rgb[i].g, rgb[i].b);
|
|
}
|
|
|
|
class RGB rgb[16];
|
|
};
|
|
|
|
class PCXHEADER
|
|
{
|
|
public:
|
|
PCXHEADER();
|
|
|
|
inline int width() const
|
|
{
|
|
return (XMax - XMin) + 1;
|
|
}
|
|
inline int height() const
|
|
{
|
|
return (YMax - YMin) + 1;
|
|
}
|
|
inline bool isCompressed() const
|
|
{
|
|
return (Encoding == 1);
|
|
}
|
|
|
|
quint8 Manufacturer; // Constant Flag, 10 = ZSoft .pcx
|
|
quint8 Version; // Version information·
|
|
// 0 = Version 2.5 of PC Paintbrush·
|
|
// 2 = Version 2.8 w/palette information·
|
|
// 3 = Version 2.8 w/o palette information·
|
|
// 4 = PC Paintbrush for Windows(Plus for
|
|
// Windows uses Ver 5)·
|
|
// 5 = Version 3.0 and > of PC Paintbrush
|
|
// and PC Paintbrush +, includes
|
|
// Publisher's Paintbrush . Includes
|
|
// 24-bit .PCX files·
|
|
quint8 Encoding; // 1 = .PCX run length encoding
|
|
quint8 Bpp; // Number of bits to represent a pixel
|
|
// (per Plane) - 1, 2, 4, or 8·
|
|
quint16 XMin;
|
|
quint16 YMin;
|
|
quint16 XMax;
|
|
quint16 YMax;
|
|
quint16 HDpi;
|
|
quint16 YDpi;
|
|
Palette ColorMap;
|
|
quint8 Reserved; // Should be set to 0.
|
|
quint8 NPlanes; // Number of color planes
|
|
quint16 BytesPerLine; // Number of bytes to allocate for a scanline
|
|
// plane. MUST be an EVEN number. Do NOT
|
|
// calculate from Xmax-Xmin.·
|
|
quint16 PaletteInfo; // How to interpret palette- 1 = Color/BW,
|
|
// 2 = Grayscale ( ignored in PB IV/ IV + )·
|
|
quint16 HScreenSize; // Horizontal screen size in pixels. New field
|
|
// found only in PB IV/IV Plus
|
|
quint16 VScreenSize; // Vertical screen size in pixels. New field
|
|
// found only in PB IV/IV Plus
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
static QDataStream &operator>>(QDataStream &s, RGB &rgb)
|
|
{
|
|
quint8 r;
|
|
quint8 g;
|
|
quint8 b;
|
|
|
|
s >> r >> g >> b;
|
|
rgb.r = r;
|
|
rgb.g = g;
|
|
rgb.b = b;
|
|
|
|
return s;
|
|
}
|
|
|
|
static QDataStream &operator>>(QDataStream &s, Palette &pal)
|
|
{
|
|
for (int i = 0; i < 16; ++i) {
|
|
s >> pal.rgb[i];
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static QDataStream &operator>>(QDataStream &s, PCXHEADER &ph)
|
|
{
|
|
quint8 m;
|
|
quint8 ver;
|
|
quint8 enc;
|
|
quint8 bpp;
|
|
s >> m >> ver >> enc >> bpp;
|
|
ph.Manufacturer = m;
|
|
ph.Version = ver;
|
|
ph.Encoding = enc;
|
|
ph.Bpp = bpp;
|
|
quint16 xmin;
|
|
quint16 ymin;
|
|
quint16 xmax;
|
|
quint16 ymax;
|
|
s >> xmin >> ymin >> xmax >> ymax;
|
|
ph.XMin = xmin;
|
|
ph.YMin = ymin;
|
|
ph.XMax = xmax;
|
|
ph.YMax = ymax;
|
|
quint16 hdpi;
|
|
quint16 ydpi;
|
|
s >> hdpi >> ydpi;
|
|
ph.HDpi = hdpi;
|
|
ph.YDpi = ydpi;
|
|
Palette colorMap;
|
|
quint8 res;
|
|
quint8 np;
|
|
s >> colorMap >> res >> np;
|
|
ph.ColorMap = colorMap;
|
|
ph.Reserved = res;
|
|
ph.NPlanes = np;
|
|
quint16 bytesperline;
|
|
s >> bytesperline;
|
|
ph.BytesPerLine = bytesperline;
|
|
quint16 paletteinfo;
|
|
s >> paletteinfo;
|
|
ph.PaletteInfo = paletteinfo;
|
|
quint16 hscreensize;
|
|
quint16 vscreensize;
|
|
s >> hscreensize;
|
|
ph.HScreenSize = hscreensize;
|
|
s >> vscreensize;
|
|
ph.VScreenSize = vscreensize;
|
|
|
|
// Skip the rest of the header
|
|
quint8 byte;
|
|
for (auto i = 0; i < 54; ++i) {
|
|
s >> byte;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static QDataStream &operator<<(QDataStream &s, const RGB rgb)
|
|
{
|
|
s << rgb.r << rgb.g << rgb.b;
|
|
|
|
return s;
|
|
}
|
|
|
|
static QDataStream &operator<<(QDataStream &s, const Palette &pal)
|
|
{
|
|
for (int i = 0; i < 16; ++i) {
|
|
s << pal.rgb[i];
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static QDataStream &operator<<(QDataStream &s, const PCXHEADER &ph)
|
|
{
|
|
s << ph.Manufacturer;
|
|
s << ph.Version;
|
|
s << ph.Encoding;
|
|
s << ph.Bpp;
|
|
s << ph.XMin << ph.YMin << ph.XMax << ph.YMax;
|
|
s << ph.HDpi << ph.YDpi;
|
|
s << ph.ColorMap;
|
|
s << ph.Reserved;
|
|
s << ph.NPlanes;
|
|
s << ph.BytesPerLine;
|
|
s << ph.PaletteInfo;
|
|
s << ph.HScreenSize;
|
|
s << ph.VScreenSize;
|
|
|
|
quint8 byte = 0;
|
|
for (int i = 0; i < 54; ++i) {
|
|
s << byte;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
PCXHEADER::PCXHEADER()
|
|
{
|
|
// Initialize all data to zero
|
|
QByteArray dummy(128, 0);
|
|
dummy.fill(0);
|
|
QDataStream s(&dummy, QIODevice::ReadOnly);
|
|
s >> *this;
|
|
}
|
|
|
|
static bool readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
|
|
{
|
|
quint32 i = 0;
|
|
quint32 size = buf.size();
|
|
quint8 byte;
|
|
quint8 count;
|
|
|
|
if (header.isCompressed()) {
|
|
// Uncompress the image data
|
|
while (i < size) {
|
|
count = 1;
|
|
s >> byte;
|
|
if (byte > 0xc0) {
|
|
count = byte - 0xc0;
|
|
s >> byte;
|
|
}
|
|
while (count-- && i < size) {
|
|
buf[i++] = byte;
|
|
}
|
|
}
|
|
} else {
|
|
// Image is not compressed (possible?)
|
|
while (i < size) {
|
|
s >> byte;
|
|
buf[i++] = byte;
|
|
}
|
|
}
|
|
|
|
return (s.status() == QDataStream::Ok);
|
|
}
|
|
|
|
static bool readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|
{
|
|
QByteArray buf(header.BytesPerLine, 0);
|
|
|
|
img = imageAlloc(header.width(), header.height(), QImage::Format_Mono);
|
|
img.setColorCount(2);
|
|
|
|
if (img.isNull()) {
|
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
|
return false;
|
|
}
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
if (s.atEnd()) {
|
|
return false;
|
|
}
|
|
|
|
if (!readLine(s, buf, header)) {
|
|
return false;
|
|
}
|
|
|
|
uchar *p = img.scanLine(y);
|
|
unsigned int bpl = qMin((quint16)((header.width() + 7) / 8), header.BytesPerLine);
|
|
for (unsigned int x = 0; x < bpl; ++x) {
|
|
p[x] = buf[x];
|
|
}
|
|
}
|
|
|
|
// Set the color palette
|
|
img.setColor(0, qRgb(0, 0, 0));
|
|
img.setColor(1, qRgb(255, 255, 255));
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|
{
|
|
QByteArray buf(header.BytesPerLine * 4, 0);
|
|
QByteArray pixbuf(header.width(), 0);
|
|
|
|
img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
|
|
img.setColorCount(16);
|
|
if (img.isNull()) {
|
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
|
return false;
|
|
}
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
if (s.atEnd()) {
|
|
return false;
|
|
}
|
|
|
|
pixbuf.fill(0);
|
|
if (!readLine(s, buf, header)) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
quint32 offset = i * header.BytesPerLine;
|
|
for (int x = 0; x < header.width(); ++x) {
|
|
if (buf[offset + (x / 8)] & (128 >> (x % 8))) {
|
|
pixbuf[x] = (int)(pixbuf[x]) + (1 << i);
|
|
}
|
|
}
|
|
}
|
|
|
|
uchar *p = img.scanLine(y);
|
|
if (!p) {
|
|
qWarning() << "Failed to get scanline for" << y << "might be out of bounds";
|
|
}
|
|
for (int x = 0; x < header.width(); ++x) {
|
|
p[x] = pixbuf[x];
|
|
}
|
|
}
|
|
|
|
// Read the palette
|
|
for (int i = 0; i < 16; ++i) {
|
|
img.setColor(i, header.ColorMap.color(i));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|
{
|
|
QByteArray buf(header.BytesPerLine, 0);
|
|
|
|
img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
|
|
img.setColorCount(256);
|
|
|
|
if (img.isNull()) {
|
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
|
return false;
|
|
}
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
if (s.atEnd()) {
|
|
return false;
|
|
}
|
|
|
|
if (!readLine(s, buf, header)) {
|
|
return false;
|
|
}
|
|
|
|
uchar *p = img.scanLine(y);
|
|
if (!p) {
|
|
return false;
|
|
}
|
|
|
|
unsigned int bpl = qMin(header.BytesPerLine, (quint16)header.width());
|
|
for (unsigned int x = 0; x < bpl; ++x) {
|
|
p[x] = buf[x];
|
|
}
|
|
}
|
|
|
|
// by specification, the extended palette starts at file.size() - 769
|
|
quint8 flag = 0;
|
|
if (auto device = s.device()) {
|
|
if (device->isSequential()) {
|
|
while (flag != 12 && s.status() == QDataStream::Ok) {
|
|
s >> flag;
|
|
}
|
|
}
|
|
else {
|
|
device->seek(device->size() - 769);
|
|
s >> flag;
|
|
}
|
|
}
|
|
|
|
// qDebug() << "Palette Flag: " << flag;
|
|
if (flag == 12 && (header.Version == 5 || header.Version == 2)) {
|
|
// Read the palette
|
|
quint8 r;
|
|
quint8 g;
|
|
quint8 b;
|
|
for (int i = 0; i < 256; ++i) {
|
|
s >> r >> g >> b;
|
|
img.setColor(i, qRgb(r, g, b));
|
|
}
|
|
}
|
|
|
|
return (s.status() == QDataStream::Ok);
|
|
}
|
|
|
|
static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|
{
|
|
QByteArray r_buf(header.BytesPerLine, 0);
|
|
QByteArray g_buf(header.BytesPerLine, 0);
|
|
QByteArray b_buf(header.BytesPerLine, 0);
|
|
|
|
img = imageAlloc(header.width(), header.height(), QImage::Format_RGB32);
|
|
|
|
if (img.isNull()) {
|
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
|
return false;
|
|
}
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
if (s.atEnd()) {
|
|
return false;
|
|
}
|
|
|
|
if (!readLine(s, r_buf, header)) {
|
|
return false;
|
|
}
|
|
if (!readLine(s, g_buf, header)) {
|
|
return false;
|
|
}
|
|
if (!readLine(s, b_buf, header)) {
|
|
return false;
|
|
}
|
|
|
|
uint *p = (uint *)img.scanLine(y);
|
|
for (int x = 0; x < header.width(); ++x) {
|
|
p[x] = qRgb(r_buf[x], g_buf[x], b_buf[x]);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool writeLine(QDataStream &s, QByteArray &buf)
|
|
{
|
|
quint32 i = 0;
|
|
quint32 size = buf.size();
|
|
quint8 count;
|
|
quint8 data;
|
|
char byte;
|
|
|
|
while (i < size) {
|
|
count = 1;
|
|
byte = buf[i++];
|
|
|
|
while ((i < size) && (byte == buf[i]) && (count < 63)) {
|
|
++i;
|
|
++count;
|
|
}
|
|
|
|
data = byte;
|
|
|
|
if (count > 1 || data >= 0xc0) {
|
|
count |= 0xc0;
|
|
s << count;
|
|
}
|
|
|
|
s << data;
|
|
}
|
|
return (s.status() == QDataStream::Ok);
|
|
}
|
|
|
|
static bool writeImage1(QImage &img, QDataStream &s, PCXHEADER &header)
|
|
{
|
|
if (img.format() != QImage::Format_Mono) {
|
|
img = img.convertToFormat(QImage::Format_Mono);
|
|
}
|
|
if (img.isNull() || img.colorCount() < 1) {
|
|
return false;
|
|
}
|
|
auto rgb = img.color(0);
|
|
auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127;
|
|
|
|
header.Bpp = 1;
|
|
header.NPlanes = 1;
|
|
header.BytesPerLine = img.bytesPerLine();
|
|
if (header.BytesPerLine == 0) {
|
|
return false;
|
|
}
|
|
|
|
s << header;
|
|
|
|
QByteArray buf(header.BytesPerLine, 0);
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
quint8 *p = img.scanLine(y);
|
|
|
|
// Invert as QImage uses reverse palette for monochrome images?
|
|
for (int i = 0; i < header.BytesPerLine; ++i) {
|
|
buf[i] = minIsBlack ? p[i] : ~p[i];
|
|
}
|
|
|
|
if (!writeLine(s, buf)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header)
|
|
{
|
|
header.Bpp = 1;
|
|
header.NPlanes = 4;
|
|
header.BytesPerLine = header.width() / 8;
|
|
if (header.BytesPerLine == 0) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
header.ColorMap.setColor(i, img.color(i));
|
|
}
|
|
|
|
s << header;
|
|
|
|
QByteArray buf[4];
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
buf[i].resize(header.BytesPerLine);
|
|
}
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
quint8 *p = img.scanLine(y);
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
buf[i].fill(0);
|
|
}
|
|
|
|
for (int x = 0; x < header.width(); ++x) {
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (*(p + x) & (1 << i)) {
|
|
buf[i][x / 8] = (int)(buf[i][x / 8]) | 1 << (7 - x % 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (!writeLine(s, buf[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
|
|
{
|
|
header.Bpp = 8;
|
|
header.NPlanes = 1;
|
|
header.BytesPerLine = img.bytesPerLine();
|
|
if (header.BytesPerLine == 0) {
|
|
return false;
|
|
}
|
|
|
|
s << header;
|
|
|
|
QByteArray buf(header.BytesPerLine, 0);
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
quint8 *p = img.scanLine(y);
|
|
|
|
for (int i = 0; i < header.BytesPerLine; ++i) {
|
|
buf[i] = p[i];
|
|
}
|
|
|
|
if (!writeLine(s, buf)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Write palette flag
|
|
quint8 byte = 12;
|
|
s << byte;
|
|
|
|
// Write palette
|
|
for (int i = 0; i < 256; ++i) {
|
|
s << RGB::from(img.color(i));
|
|
}
|
|
|
|
return (s.status() == QDataStream::Ok);
|
|
}
|
|
|
|
static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
|
|
{
|
|
header.Bpp = 8;
|
|
header.NPlanes = 3;
|
|
header.BytesPerLine = header.width();
|
|
if (header.BytesPerLine == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB32) {
|
|
img = img.convertToFormat(QImage::Format_RGB32);
|
|
}
|
|
if (img.isNull()) {
|
|
return false;
|
|
}
|
|
|
|
s << header;
|
|
|
|
QByteArray r_buf(header.width(), 0);
|
|
QByteArray g_buf(header.width(), 0);
|
|
QByteArray b_buf(header.width(), 0);
|
|
|
|
for (int y = 0; y < header.height(); ++y) {
|
|
auto p = (QRgb*)img.scanLine(y);
|
|
|
|
for (int x = 0; x < header.width(); ++x) {
|
|
QRgb rgb = *p++;
|
|
r_buf[x] = qRed(rgb);
|
|
g_buf[x] = qGreen(rgb);
|
|
b_buf[x] = qBlue(rgb);
|
|
}
|
|
|
|
if (!writeLine(s, r_buf)) {
|
|
return false;
|
|
}
|
|
if (!writeLine(s, g_buf)) {
|
|
return false;
|
|
}
|
|
if (!writeLine(s, b_buf)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
PCXHandler::PCXHandler()
|
|
{
|
|
}
|
|
|
|
bool PCXHandler::canRead() const
|
|
{
|
|
if (canRead(device())) {
|
|
setFormat("pcx");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PCXHandler::read(QImage *outImage)
|
|
{
|
|
QDataStream s(device());
|
|
s.setByteOrder(QDataStream::LittleEndian);
|
|
|
|
if (s.device()->size() < 128) {
|
|
return false;
|
|
}
|
|
|
|
PCXHEADER header;
|
|
|
|
s >> header;
|
|
|
|
if (header.Manufacturer != 10 || header.BytesPerLine == 0 || s.atEnd()) {
|
|
return false;
|
|
}
|
|
|
|
auto ok = false;
|
|
QImage img;
|
|
if (header.Bpp == 1 && header.NPlanes == 1) {
|
|
ok = readImage1(img, s, header);
|
|
} else if (header.Bpp == 1 && header.NPlanes == 4) {
|
|
ok = readImage4(img, s, header);
|
|
} else if (header.Bpp == 8 && header.NPlanes == 1) {
|
|
ok = readImage8(img, s, header);
|
|
} else if (header.Bpp == 8 && header.NPlanes == 3) {
|
|
ok = readImage24(img, s, header);
|
|
}
|
|
|
|
if (img.isNull() || !ok) {
|
|
return false;
|
|
}
|
|
|
|
img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000));
|
|
img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000));
|
|
*outImage = img;
|
|
return true;
|
|
}
|
|
|
|
bool PCXHandler::write(const QImage &image)
|
|
{
|
|
QDataStream s(device());
|
|
s.setByteOrder(QDataStream::LittleEndian);
|
|
|
|
QImage img = image;
|
|
|
|
const int w = img.width();
|
|
const int h = img.height();
|
|
|
|
if (w > 65536 || h > 65536) {
|
|
return false;
|
|
}
|
|
|
|
PCXHEADER header;
|
|
|
|
header.Manufacturer = 10;
|
|
header.Version = 5;
|
|
header.Encoding = 1;
|
|
header.XMin = 0;
|
|
header.YMin = 0;
|
|
header.XMax = w - 1;
|
|
header.YMax = h - 1;
|
|
header.HDpi = qRound(image.dotsPerMeterX() * 25.4 / 1000);
|
|
header.YDpi = qRound(image.dotsPerMeterY() * 25.4 / 1000);
|
|
header.Reserved = 0;
|
|
header.PaletteInfo = 1;
|
|
|
|
auto ok = false;
|
|
if (img.depth() == 1) {
|
|
ok = writeImage1(img, s, header);
|
|
} else if (img.depth() == 8 && img.colorCount() <= 16) {
|
|
ok = writeImage4(img, s, header);
|
|
} else if (img.depth() == 8) {
|
|
ok = writeImage8(img, s, header);
|
|
} else if (img.depth() >= 24) {
|
|
ok = writeImage24(img, s, header);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool PCXHandler::canRead(QIODevice *device)
|
|
{
|
|
if (!device) {
|
|
qWarning("PCXHandler::canRead() called with no device");
|
|
return false;
|
|
}
|
|
|
|
qint64 oldPos = device->pos();
|
|
|
|
char head[1];
|
|
qint64 readBytes = device->read(head, sizeof(head));
|
|
if (readBytes != sizeof(head)) {
|
|
if (device->isSequential()) {
|
|
while (readBytes > 0) {
|
|
device->ungetChar(head[readBytes-- - 1]);
|
|
}
|
|
} else {
|
|
device->seek(oldPos);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (device->isSequential()) {
|
|
while (readBytes > 0) {
|
|
device->ungetChar(head[readBytes-- - 1]);
|
|
}
|
|
} else {
|
|
device->seek(oldPos);
|
|
}
|
|
|
|
return qstrncmp(head, "\012", 1) == 0;
|
|
}
|
|
|
|
QImageIOPlugin::Capabilities PCXPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
|
{
|
|
if (format == "pcx") {
|
|
return Capabilities(CanRead | CanWrite);
|
|
}
|
|
if (!format.isEmpty()) {
|
|
return {};
|
|
}
|
|
if (!device->isOpen()) {
|
|
return {};
|
|
}
|
|
|
|
Capabilities cap;
|
|
if (device->isReadable() && PCXHandler::canRead(device)) {
|
|
cap |= CanRead;
|
|
}
|
|
if (device->isWritable()) {
|
|
cap |= CanWrite;
|
|
}
|
|
return cap;
|
|
}
|
|
|
|
QImageIOHandler *PCXPlugin::create(QIODevice *device, const QByteArray &format) const
|
|
{
|
|
QImageIOHandler *handler = new PCXHandler;
|
|
handler->setDevice(device);
|
|
handler->setFormat(format);
|
|
return handler;
|
|
}
|