-
Notifications
You must be signed in to change notification settings - Fork 183
Expand file tree
/
Copy pathImageRenderer.cpp
More file actions
123 lines (102 loc) · 3.85 KB
/
Copy pathImageRenderer.cpp
File metadata and controls
123 lines (102 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "ImageRenderer.h"
#include <bmpman/bmpman.h> // bm_load, bm_get_info, bm_lock, bm_unlock
#include <ddsutils/ddsutils.h>
#include <QtGlobal>
namespace fso::fred::util {
static void setError(QString* outError, const QString& text)
{
if (outError)
*outError = text;
}
// bm_lock_dds keeps compressed data as-is when the renderer reports s3tc/BPTC
// support, which would crash the regular 32-bpp QImage path. For the picker
// preview, ask ddsutils to decompress the top mip directly.
static bool decompressDdsToQImage(const char* bm_filename, QImage& outImage, QString* outError)
{
int w = 0, h = 0;
SCP_vector<ubyte> pixels;
const int err = dds_decompress_top_mip_bgra(bm_filename, CF_TYPE_ANY, &w, &h, pixels);
if (err != DDS_ERROR_NONE) {
setError(outError, QStringLiteral("DDS decompress failed (%1).").arg(err));
return false;
}
QImage tmp(pixels.data(), w, h, w * 4, QImage::Format_ARGB32);
outImage = tmp.copy(); // detach before `pixels` goes out of scope
return !outImage.isNull();
}
bool loadHandleToQImage(int bmHandle, QImage& outImage, QString* outError)
{
outImage = QImage(); // clear
if (bmHandle < 0) {
setError(outError, QStringLiteral("Invalid bitmap handle."));
return false;
}
if (bm_is_compressed(bmHandle)) {
const char* fname = bm_get_filename(bmHandle);
if (fname && *fname)
return decompressDdsToQImage(fname, outImage, outError);
setError(outError, QStringLiteral("Compressed DDS with no filename; cannot preview."));
return false;
}
int w = 0, h = 0;
if (bm_get_info(bmHandle, &w, &h) < 0 || w <= 0 || h <= 0) {
setError(outError, QStringLiteral("Bitmap has invalid info."));
return false;
}
// All FSO animation types (ANI, APNG, EFF) produce BGRA byte-order data
// at 32 bpp, which matches QImage::Format_ARGB32 on little-endian.
auto* bmp = bm_lock(bmHandle, 32, BMP_TEX_XPARENT);
if (bmp == nullptr) {
setError(outError, QStringLiteral("bm_lock failed."));
return false;
}
if (bmp->data == 0) {
// bm_lock incremented the refcount before populating data; release it.
bm_unlock(bmHandle);
setError(outError, QStringLiteral("bm_lock failed."));
return false;
}
// bm_lock ignores the requested bpp for JPG (always 24, BGR) and for
// uncompressed DDS (whatever the file uses). Handle the two common
// cases (32-bpp BGRA and 24-bpp BGR) and reject anything else.
if (bmp->bpp == 32) {
const int bytesPerLine = bmp->w * 4;
QImage tmp(reinterpret_cast<const uchar*>(bmp->data), bmp->w, bmp->h, bytesPerLine, QImage::Format_ARGB32);
outImage = tmp.copy(); // detach from bmpman memory before unlock
} else if (bmp->bpp == 24) {
const int bytesPerLine = bmp->w * 3;
QImage tmp(reinterpret_cast<const uchar*>(bmp->data), bmp->w, bmp->h, bytesPerLine, QImage::Format_RGB888);
// FSO stores 24-bpp as BGR; swap to RGB and promote to ARGB32 (also detaches).
outImage = tmp.rgbSwapped().convertToFormat(QImage::Format_ARGB32);
} else {
bm_unlock(bmHandle);
setError(outError, QStringLiteral("Unsupported bitmap bpp (%1) for QImage preview.").arg(bmp->bpp));
return false;
}
bm_unlock(bmHandle);
if (outImage.isNull()) {
setError(outError, QStringLiteral("Failed to construct QImage."));
return false;
}
return true;
}
bool loadImageToQImage(const std::string& filename, QImage& outImage, QString* outError)
{
outImage = QImage();
if (filename.empty()) {
setError(outError, QStringLiteral("Empty filename."));
return false;
}
// Let bmpman resolve the file
int handle = bm_load(filename.c_str());
if (handle < 0) {
setError(outError, QStringLiteral("bm_load failed for \"%1\".").arg(QString::fromStdString(filename)));
return false;
}
const bool ok = loadHandleToQImage(handle, outImage, outError);
// bm_unload is load_count aware, so if another
// part of qtfred is sharing the handle it stays alive for them.
bm_unload(handle);
return ok;
}
} // namespace fso::fred::util