| 1 | /** |
| 2 | * Copyright (c) 2006-2023 LOVE Development Team |
| 3 | * |
| 4 | * This software is provided 'as-is', without any express or implied |
| 5 | * warranty. In no event will the authors be held liable for any damages |
| 6 | * arising from the use of this software. |
| 7 | * |
| 8 | * Permission is granted to anyone to use this software for any purpose, |
| 9 | * including commercial applications, and to alter it and redistribute it |
| 10 | * freely, subject to the following restrictions: |
| 11 | * |
| 12 | * 1. The origin of this software must not be misrepresented; you must not |
| 13 | * claim that you wrote the original software. If you use this software |
| 14 | * in a product, an acknowledgment in the product documentation would be |
| 15 | * appreciated but is not required. |
| 16 | * 2. Altered source versions must be plainly marked as such, and must not be |
| 17 | * misrepresented as being the original software. |
| 18 | * 3. This notice may not be removed or altered from any source distribution. |
| 19 | **/ |
| 20 | |
| 21 | #include "ImageData.h" |
| 22 | #include "Image.h" |
| 23 | #include "filesystem/Filesystem.h" |
| 24 | |
| 25 | #include <algorithm> // min/max |
| 26 | |
| 27 | using love::thread::Lock; |
| 28 | |
| 29 | namespace love |
| 30 | { |
| 31 | namespace image |
| 32 | { |
| 33 | |
| 34 | love::Type ImageData::type("ImageData" , &Data::type); |
| 35 | |
| 36 | ImageData::ImageData(Data *data) |
| 37 | : ImageDataBase(PIXELFORMAT_UNKNOWN, 0, 0) |
| 38 | { |
| 39 | decode(data); |
| 40 | } |
| 41 | |
| 42 | ImageData::ImageData(int width, int height, PixelFormat format) |
| 43 | : ImageDataBase(format, width, height) |
| 44 | { |
| 45 | if (!validPixelFormat(format)) |
| 46 | throw love::Exception("Unsupported pixel format for ImageData" ); |
| 47 | |
| 48 | create(width, height, format); |
| 49 | |
| 50 | // Set to black/transparency. |
| 51 | memset(data, 0, getSize()); |
| 52 | } |
| 53 | |
| 54 | ImageData::ImageData(int width, int height, PixelFormat format, void *data, bool own) |
| 55 | : ImageDataBase(format, width, height) |
| 56 | { |
| 57 | if (!validPixelFormat(format)) |
| 58 | throw love::Exception("Unsupported pixel format for ImageData" ); |
| 59 | |
| 60 | if (own) |
| 61 | this->data = (unsigned char *) data; |
| 62 | else |
| 63 | create(width, height, format, data); |
| 64 | } |
| 65 | |
| 66 | ImageData::ImageData(const ImageData &c) |
| 67 | : ImageDataBase(c.format, c.width, c.height) |
| 68 | { |
| 69 | create(width, height, format, c.getData()); |
| 70 | } |
| 71 | |
| 72 | ImageData::~ImageData() |
| 73 | { |
| 74 | if (decodeHandler.get()) |
| 75 | decodeHandler->freeRawPixels(data); |
| 76 | else |
| 77 | delete[] data; |
| 78 | } |
| 79 | |
| 80 | love::image::ImageData *ImageData::clone() const |
| 81 | { |
| 82 | return new ImageData(*this); |
| 83 | } |
| 84 | |
| 85 | void ImageData::create(int width, int height, PixelFormat format, void *data) |
| 86 | { |
| 87 | size_t datasize = width * height * getPixelFormatSize(format); |
| 88 | |
| 89 | try |
| 90 | { |
| 91 | this->data = new unsigned char[datasize]; |
| 92 | } |
| 93 | catch(std::bad_alloc &) |
| 94 | { |
| 95 | throw love::Exception("Out of memory" ); |
| 96 | } |
| 97 | |
| 98 | if (data) |
| 99 | memcpy(this->data, data, datasize); |
| 100 | |
| 101 | decodeHandler = nullptr; |
| 102 | this->format = format; |
| 103 | |
| 104 | pixelSetFunction = getPixelSetFunction(format); |
| 105 | pixelGetFunction = getPixelGetFunction(format); |
| 106 | } |
| 107 | |
| 108 | void ImageData::decode(Data *data) |
| 109 | { |
| 110 | FormatHandler *decoder = nullptr; |
| 111 | FormatHandler::DecodedImage decodedimage; |
| 112 | |
| 113 | auto module = Module::getInstance<Image>(Module::M_IMAGE); |
| 114 | |
| 115 | if (module == nullptr) |
| 116 | throw love::Exception("love.image must be loaded in order to decode an ImageData." ); |
| 117 | |
| 118 | for (FormatHandler *handler : module->getFormatHandlers()) |
| 119 | { |
| 120 | if (handler->canDecode(data)) |
| 121 | { |
| 122 | decoder = handler; |
| 123 | break; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | if (decoder) |
| 128 | decodedimage = decoder->decode(data); |
| 129 | |
| 130 | if (decodedimage.data == nullptr) |
| 131 | { |
| 132 | auto filedata = dynamic_cast<filesystem::FileData *>(data); |
| 133 | |
| 134 | if (filedata != nullptr) |
| 135 | { |
| 136 | const std::string &name = filedata->getFilename(); |
| 137 | throw love::Exception("Could not decode file '%s' to ImageData: unsupported file format" , name.c_str()); |
| 138 | } |
| 139 | else |
| 140 | throw love::Exception("Could not decode data to ImageData: unsupported encoded format" ); |
| 141 | } |
| 142 | |
| 143 | if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelFormatSize(decodedimage.format)) |
| 144 | { |
| 145 | decoder->freeRawPixels(decodedimage.data); |
| 146 | throw love::Exception("Could not convert image!" ); |
| 147 | } |
| 148 | |
| 149 | // Clean up any old data. |
| 150 | if (decodeHandler) |
| 151 | decodeHandler->freeRawPixels(this->data); |
| 152 | else |
| 153 | delete[] this->data; |
| 154 | |
| 155 | this->width = decodedimage.width; |
| 156 | this->height = decodedimage.height; |
| 157 | this->data = decodedimage.data; |
| 158 | this->format = decodedimage.format; |
| 159 | |
| 160 | decodeHandler = decoder; |
| 161 | |
| 162 | pixelSetFunction = getPixelSetFunction(format); |
| 163 | pixelGetFunction = getPixelGetFunction(format); |
| 164 | } |
| 165 | |
| 166 | love::filesystem::FileData *ImageData::encode(FormatHandler::EncodedFormat encodedFormat, const char *filename, bool writefile) const |
| 167 | { |
| 168 | FormatHandler *encoder = nullptr; |
| 169 | FormatHandler::EncodedImage encodedimage; |
| 170 | FormatHandler::DecodedImage rawimage; |
| 171 | |
| 172 | rawimage.width = width; |
| 173 | rawimage.height = height; |
| 174 | rawimage.size = getSize(); |
| 175 | rawimage.data = data; |
| 176 | rawimage.format = format; |
| 177 | |
| 178 | auto module = Module::getInstance<Image>(Module::M_IMAGE); |
| 179 | |
| 180 | if (module == nullptr) |
| 181 | throw love::Exception("love.image must be loaded in order to encode an ImageData." ); |
| 182 | |
| 183 | for (FormatHandler *handler : module->getFormatHandlers()) |
| 184 | { |
| 185 | if (handler->canEncode(format, encodedFormat)) |
| 186 | { |
| 187 | encoder = handler; |
| 188 | break; |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | if (encoder != nullptr) |
| 193 | { |
| 194 | thread::Lock lock(mutex); |
| 195 | encodedimage = encoder->encode(rawimage, encodedFormat); |
| 196 | } |
| 197 | |
| 198 | if (encoder == nullptr || encodedimage.data == nullptr) |
| 199 | { |
| 200 | const char *fname = "unknown" ; |
| 201 | love::getConstant(format, fname); |
| 202 | throw love::Exception("No suitable image encoder for %s format." , fname); |
| 203 | } |
| 204 | |
| 205 | love::filesystem::FileData *filedata = nullptr; |
| 206 | |
| 207 | try |
| 208 | { |
| 209 | filedata = new love::filesystem::FileData(encodedimage.size, filename); |
| 210 | } |
| 211 | catch (love::Exception &) |
| 212 | { |
| 213 | encoder->freeRawPixels(encodedimage.data); |
| 214 | throw; |
| 215 | } |
| 216 | |
| 217 | memcpy(filedata->getData(), encodedimage.data, encodedimage.size); |
| 218 | encoder->freeRawPixels(encodedimage.data); |
| 219 | |
| 220 | if (writefile) |
| 221 | { |
| 222 | auto fs = Module::getInstance<filesystem::Filesystem>(Module::M_FILESYSTEM); |
| 223 | |
| 224 | if (fs == nullptr) |
| 225 | { |
| 226 | filedata->release(); |
| 227 | throw love::Exception("love.filesystem must be loaded in order to write an encoded ImageData to a file." ); |
| 228 | } |
| 229 | |
| 230 | try |
| 231 | { |
| 232 | fs->write(filename, filedata->getData(), filedata->getSize()); |
| 233 | } |
| 234 | catch (love::Exception &) |
| 235 | { |
| 236 | filedata->release(); |
| 237 | throw; |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | return filedata; |
| 242 | } |
| 243 | |
| 244 | size_t ImageData::getSize() const |
| 245 | { |
| 246 | return size_t(getWidth() * getHeight()) * getPixelSize(); |
| 247 | } |
| 248 | |
| 249 | void *ImageData::getData() const |
| 250 | { |
| 251 | return data; |
| 252 | } |
| 253 | |
| 254 | bool ImageData::isSRGB() const |
| 255 | { |
| 256 | return false; |
| 257 | } |
| 258 | |
| 259 | bool ImageData::inside(int x, int y) const |
| 260 | { |
| 261 | return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); |
| 262 | } |
| 263 | |
| 264 | static float clamp01(float x) |
| 265 | { |
| 266 | return std::min(std::max(x, 0.0f), 1.0f); |
| 267 | } |
| 268 | |
| 269 | static void setPixelR8(const Colorf &c, ImageData::Pixel *p) |
| 270 | { |
| 271 | p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f); |
| 272 | } |
| 273 | |
| 274 | static void setPixelRG8(const Colorf &c, ImageData::Pixel *p) |
| 275 | { |
| 276 | p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f); |
| 277 | p->rgba8[1] = (uint8) (clamp01(c.g) * 255.0f + 0.5f); |
| 278 | } |
| 279 | |
| 280 | static void setPixelRGBA8(const Colorf &c, ImageData::Pixel *p) |
| 281 | { |
| 282 | p->rgba8[0] = (uint8) (clamp01(c.r) * 255.0f + 0.5f); |
| 283 | p->rgba8[1] = (uint8) (clamp01(c.g) * 255.0f + 0.5f); |
| 284 | p->rgba8[2] = (uint8) (clamp01(c.b) * 255.0f + 0.5f); |
| 285 | p->rgba8[3] = (uint8) (clamp01(c.a) * 255.0f + 0.5f); |
| 286 | } |
| 287 | |
| 288 | static void setPixelR16(const Colorf &c, ImageData::Pixel *p) |
| 289 | { |
| 290 | p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f); |
| 291 | } |
| 292 | |
| 293 | static void setPixelRG16(const Colorf &c, ImageData::Pixel *p) |
| 294 | { |
| 295 | p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f); |
| 296 | p->rgba16[1] = (uint16) (clamp01(c.g) * 65535.0f + 0.5f); |
| 297 | } |
| 298 | |
| 299 | static void setPixelRGBA16(const Colorf &c, ImageData::Pixel *p) |
| 300 | { |
| 301 | p->rgba16[0] = (uint16) (clamp01(c.r) * 65535.0f + 0.5f); |
| 302 | p->rgba16[1] = (uint16) (clamp01(c.b) * 65535.0f + 0.5f); |
| 303 | p->rgba16[2] = (uint16) (clamp01(c.g) * 65535.0f + 0.5f); |
| 304 | p->rgba16[3] = (uint16) (clamp01(c.a) * 65535.0f + 0.5f); |
| 305 | } |
| 306 | |
| 307 | static void setPixelR16F(const Colorf &c, ImageData::Pixel *p) |
| 308 | { |
| 309 | p->rgba16f[0] = float32to16(c.r); |
| 310 | } |
| 311 | |
| 312 | static void setPixelRG16F(const Colorf &c, ImageData::Pixel *p) |
| 313 | { |
| 314 | p->rgba16f[0] = float32to16(c.r); |
| 315 | p->rgba16f[1] = float32to16(c.g); |
| 316 | } |
| 317 | |
| 318 | static void setPixelRGBA16F(const Colorf &c, ImageData::Pixel *p) |
| 319 | { |
| 320 | p->rgba16f[0] = float32to16(c.r); |
| 321 | p->rgba16f[1] = float32to16(c.g); |
| 322 | p->rgba16f[2] = float32to16(c.b); |
| 323 | p->rgba16f[3] = float32to16(c.a); |
| 324 | } |
| 325 | |
| 326 | static void setPixelR32F(const Colorf &c, ImageData::Pixel *p) |
| 327 | { |
| 328 | p->rgba32f[0] = c.r; |
| 329 | } |
| 330 | |
| 331 | static void setPixelRG32F(const Colorf &c, ImageData::Pixel *p) |
| 332 | { |
| 333 | p->rgba32f[0] = c.r; |
| 334 | p->rgba32f[1] = c.g; |
| 335 | } |
| 336 | |
| 337 | static void setPixelRGBA32F(const Colorf &c, ImageData::Pixel *p) |
| 338 | { |
| 339 | p->rgba32f[0] = c.r; |
| 340 | p->rgba32f[1] = c.g; |
| 341 | p->rgba32f[2] = c.b; |
| 342 | p->rgba32f[3] = c.a; |
| 343 | } |
| 344 | |
| 345 | static void setPixelRGBA4(const Colorf &c, ImageData::Pixel *p) |
| 346 | { |
| 347 | // LSB->MSB: [a, b, g, r] |
| 348 | uint16 r = (uint16) (clamp01(c.r) * 0xF + 0.5); |
| 349 | uint16 g = (uint16) (clamp01(c.g) * 0xF + 0.5); |
| 350 | uint16 b = (uint16) (clamp01(c.b) * 0xF + 0.5); |
| 351 | uint16 a = (uint16) (clamp01(c.a) * 0xF + 0.5); |
| 352 | p->packed16 = (r << 12) | (g << 8) | (b << 4) | (a << 0); |
| 353 | } |
| 354 | |
| 355 | static void setPixelRGB5A1(const Colorf &c, ImageData::Pixel *p) |
| 356 | { |
| 357 | // LSB->MSB: [a, b, g, r] |
| 358 | uint16 r = (uint16) (clamp01(c.r) * 0x1F + 0.5); |
| 359 | uint16 g = (uint16) (clamp01(c.g) * 0x1F + 0.5); |
| 360 | uint16 b = (uint16) (clamp01(c.b) * 0x1F + 0.5); |
| 361 | uint16 a = (uint16) (clamp01(c.a) * 0x1 + 0.5); |
| 362 | p->packed16 = (r << 11) | (g << 6) | (b << 1) | (a << 0); |
| 363 | } |
| 364 | |
| 365 | static void setPixelRGB565(const Colorf &c, ImageData::Pixel *p) |
| 366 | { |
| 367 | // LSB->MSB: [b, g, r] |
| 368 | uint16 r = (uint16) (clamp01(c.r) * 0x1F + 0.5); |
| 369 | uint16 g = (uint16) (clamp01(c.g) * 0x3F + 0.5); |
| 370 | uint16 b = (uint16) (clamp01(c.b) * 0x1F + 0.5); |
| 371 | p->packed16 = (r << 11) | (g << 5) | (b << 0); |
| 372 | } |
| 373 | |
| 374 | static void setPixelRGB10A2(const Colorf &c, ImageData::Pixel *p) |
| 375 | { |
| 376 | // LSB->MSB: [r, g, b, a] |
| 377 | uint32 r = (uint32) (clamp01(c.r) * 0x3FF + 0.5); |
| 378 | uint32 g = (uint32) (clamp01(c.g) * 0x3FF + 0.5); |
| 379 | uint32 b = (uint32) (clamp01(c.b) * 0x3FF + 0.5); |
| 380 | uint32 a = (uint32) (clamp01(c.a) * 0x3 + 0.5); |
| 381 | p->packed32 = (r << 0) | (g << 10) | (b << 20) | (a << 30); |
| 382 | } |
| 383 | |
| 384 | static void setPixelRG11B10F(const Colorf &c, ImageData::Pixel *p) |
| 385 | { |
| 386 | // LSB->MSB: [r, g, b] |
| 387 | float11 r = float32to11(c.r); |
| 388 | float11 g = float32to11(c.g); |
| 389 | float10 b = float32to10(c.b); |
| 390 | p->packed32 = (r << 0) | (g << 11) | (b << 22); |
| 391 | } |
| 392 | |
| 393 | static void getPixelR8(const ImageData::Pixel *p, Colorf &c) |
| 394 | { |
| 395 | c.r = p->rgba8[0] / 255.0f; |
| 396 | c.g = 0.0f; |
| 397 | c.b = 0.0f; |
| 398 | c.a = 1.0f; |
| 399 | } |
| 400 | |
| 401 | static void getPixelRG8(const ImageData::Pixel *p, Colorf &c) |
| 402 | { |
| 403 | c.r = p->rgba8[0] / 255.0f; |
| 404 | c.g = p->rgba8[1] / 255.0f; |
| 405 | c.b = 0.0f; |
| 406 | c.a = 1.0f; |
| 407 | } |
| 408 | |
| 409 | static void getPixelRGBA8(const ImageData::Pixel *p, Colorf &c) |
| 410 | { |
| 411 | c.r = p->rgba8[0] / 255.0f; |
| 412 | c.g = p->rgba8[1] / 255.0f; |
| 413 | c.b = p->rgba8[2] / 255.0f; |
| 414 | c.a = p->rgba8[3] / 255.0f; |
| 415 | } |
| 416 | |
| 417 | static void getPixelR16(const ImageData::Pixel *p, Colorf &c) |
| 418 | { |
| 419 | c.r = p->rgba16[0] / 65535.0f; |
| 420 | c.g = 0.0f; |
| 421 | c.b = 0.0f; |
| 422 | c.a = 1.0f; |
| 423 | } |
| 424 | |
| 425 | static void getPixelRG16(const ImageData::Pixel *p, Colorf &c) |
| 426 | { |
| 427 | c.r = p->rgba16[0] / 65535.0f; |
| 428 | c.g = p->rgba16[1] / 65535.0f; |
| 429 | c.b = 0.0f; |
| 430 | c.a = 1.0f; |
| 431 | } |
| 432 | |
| 433 | static void getPixelRGBA16(const ImageData::Pixel *p, Colorf &c) |
| 434 | { |
| 435 | c.r = p->rgba16[0] / 65535.0f; |
| 436 | c.g = p->rgba16[1] / 65535.0f; |
| 437 | c.b = p->rgba16[2] / 65535.0f; |
| 438 | c.a = p->rgba16[3] / 65535.0f; |
| 439 | } |
| 440 | |
| 441 | static void getPixelR16F(const ImageData::Pixel *p, Colorf &c) |
| 442 | { |
| 443 | c.r = float16to32(p->rgba16f[0]); |
| 444 | c.g = 0.0f; |
| 445 | c.b = 0.0f; |
| 446 | c.a = 1.0f; |
| 447 | } |
| 448 | |
| 449 | static void getPixelRG16F(const ImageData::Pixel *p, Colorf &c) |
| 450 | { |
| 451 | c.r = float16to32(p->rgba16f[0]); |
| 452 | c.g = float16to32(p->rgba16f[1]); |
| 453 | c.b = 0.0f; |
| 454 | c.a = 1.0f; |
| 455 | } |
| 456 | |
| 457 | static void getPixelRGBA16F(const ImageData::Pixel *p, Colorf &c) |
| 458 | { |
| 459 | c.r = float16to32(p->rgba16f[0]); |
| 460 | c.g = float16to32(p->rgba16f[1]); |
| 461 | c.b = float16to32(p->rgba16f[2]); |
| 462 | c.a = float16to32(p->rgba16f[3]); |
| 463 | } |
| 464 | |
| 465 | static void getPixelR32F(const ImageData::Pixel *p, Colorf &c) |
| 466 | { |
| 467 | c.r = p->rgba32f[0]; |
| 468 | c.g = 0.0f; |
| 469 | c.b = 0.0f; |
| 470 | c.a = 1.0f; |
| 471 | } |
| 472 | |
| 473 | static void getPixelRG32F(const ImageData::Pixel *p, Colorf &c) |
| 474 | { |
| 475 | c.r = p->rgba32f[0]; |
| 476 | c.g = p->rgba32f[1]; |
| 477 | c.b = 0.0f; |
| 478 | c.a = 1.0f; |
| 479 | } |
| 480 | |
| 481 | static void getPixelRGBA32F(const ImageData::Pixel *p, Colorf &c) |
| 482 | { |
| 483 | c.r = p->rgba32f[0]; |
| 484 | c.g = p->rgba32f[1]; |
| 485 | c.b = p->rgba32f[2]; |
| 486 | c.a = p->rgba32f[3]; |
| 487 | } |
| 488 | |
| 489 | static void getPixelRGBA4(const ImageData::Pixel *p, Colorf &c) |
| 490 | { |
| 491 | // LSB->MSB: [a, b, g, r] |
| 492 | c.r = ((p->packed16 >> 12) & 0xF) / (float)0xF; |
| 493 | c.g = ((p->packed16 >> 8) & 0xF) / (float)0xF; |
| 494 | c.b = ((p->packed16 >> 4) & 0xF) / (float)0xF; |
| 495 | c.a = ((p->packed16 >> 0) & 0xF) / (float)0xF; |
| 496 | } |
| 497 | |
| 498 | static void getPixelRGB5A1(const ImageData::Pixel *p, Colorf &c) |
| 499 | { |
| 500 | // LSB->MSB: [a, b, g, r] |
| 501 | c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F; |
| 502 | c.g = ((p->packed16 >> 6) & 0x1F) / (float)0x1F; |
| 503 | c.b = ((p->packed16 >> 1) & 0x1F) / (float)0x1F; |
| 504 | c.a = ((p->packed16 >> 0) & 0x1) / (float)0x1; |
| 505 | } |
| 506 | |
| 507 | static void getPixelRGB565(const ImageData::Pixel *p, Colorf &c) |
| 508 | { |
| 509 | // LSB->MSB: [b, g, r] |
| 510 | c.r = ((p->packed16 >> 11) & 0x1F) / (float)0x1F; |
| 511 | c.g = ((p->packed16 >> 5) & 0x3F) / (float)0x3F; |
| 512 | c.b = ((p->packed16 >> 0) & 0x1F) / (float)0x1F; |
| 513 | c.a = 1.0f; |
| 514 | } |
| 515 | |
| 516 | static void getPixelRGB10A2(const ImageData::Pixel *p, Colorf &c) |
| 517 | { |
| 518 | // LSB->MSB: [r, g, b, a] |
| 519 | c.r = ((p->packed32 >> 0) & 0x3FF) / (float)0x3FF; |
| 520 | c.g = ((p->packed32 >> 10) & 0x3FF) / (float)0x3FF; |
| 521 | c.b = ((p->packed32 >> 20) & 0x3FF) / (float)0x3FF; |
| 522 | c.a = ((p->packed32 >> 30) & 0x3) / (float)0x3; |
| 523 | } |
| 524 | |
| 525 | static void getPixelRG11B10F(const ImageData::Pixel *p, Colorf &c) |
| 526 | { |
| 527 | // LSB->MSB: [r, g, b] |
| 528 | c.r = float11to32((float11) ((p->packed32 >> 0) & 0x7FF)); |
| 529 | c.g = float11to32((float11) ((p->packed32 >> 11) & 0x7FF)); |
| 530 | c.b = float10to32((float10) ((p->packed32 >> 22) & 0x3FF)); |
| 531 | c.a = 1.0f; |
| 532 | } |
| 533 | |
| 534 | void ImageData::setPixel(int x, int y, const Colorf &c) |
| 535 | { |
| 536 | if (!inside(x, y)) |
| 537 | throw love::Exception("Attempt to set out-of-range pixel!" ); |
| 538 | |
| 539 | size_t pixelsize = getPixelSize(); |
| 540 | Pixel *p = (Pixel *) (data + ((y * width + x) * pixelsize)); |
| 541 | |
| 542 | if (pixelSetFunction == nullptr) |
| 543 | throw love::Exception("Unhandled pixel format %d in ImageData::setPixel" , format); |
| 544 | |
| 545 | Lock lock(mutex); |
| 546 | |
| 547 | pixelSetFunction(c, p); |
| 548 | } |
| 549 | |
| 550 | void ImageData::getPixel(int x, int y, Colorf &c) const |
| 551 | { |
| 552 | if (!inside(x, y)) |
| 553 | throw love::Exception("Attempt to get out-of-range pixel!" ); |
| 554 | |
| 555 | size_t pixelsize = getPixelSize(); |
| 556 | const Pixel *p = (const Pixel *) (data + ((y * width + x) * pixelsize)); |
| 557 | |
| 558 | if (pixelGetFunction == nullptr) |
| 559 | throw love::Exception("Unhandled pixel format %d in ImageData::setPixel" , format); |
| 560 | |
| 561 | Lock lock(mutex); |
| 562 | |
| 563 | pixelGetFunction(p, c); |
| 564 | } |
| 565 | |
| 566 | Colorf ImageData::getPixel(int x, int y) const |
| 567 | { |
| 568 | Colorf c; |
| 569 | getPixel(x, y, c); |
| 570 | return c; |
| 571 | } |
| 572 | |
| 573 | union Row |
| 574 | { |
| 575 | uint8 *u8; |
| 576 | uint16 *u16; |
| 577 | float16 *f16; |
| 578 | float *f32; |
| 579 | }; |
| 580 | |
| 581 | static void pasteRGBA8toRGBA16(Row src, Row dst, int w) |
| 582 | { |
| 583 | for (int i = 0; i < w * 4; i++) |
| 584 | dst.u16[i] = (uint16) src.u8[i] << 8u; |
| 585 | } |
| 586 | |
| 587 | static void pasteRGBA8toRGBA16F(Row src, Row dst, int w) |
| 588 | { |
| 589 | for (int i = 0; i < w * 4; i++) |
| 590 | dst.f16[i] = float32to16(src.u8[i] / 255.0f); |
| 591 | } |
| 592 | |
| 593 | static void pasteRGBA8toRGBA32F(Row src, Row dst, int w) |
| 594 | { |
| 595 | for (int i = 0; i < w * 4; i++) |
| 596 | dst.f32[i] = src.u8[i] / 255.0f; |
| 597 | } |
| 598 | |
| 599 | static void pasteRGBA16toRGBA8(Row src, Row dst, int w) |
| 600 | { |
| 601 | for (int i = 0; i < w * 4; i++) |
| 602 | dst.u8[i] = src.u16[i] >> 8u; |
| 603 | } |
| 604 | |
| 605 | static void pasteRGBA16toRGBA16F(Row src, Row dst, int w) |
| 606 | { |
| 607 | for (int i = 0; i < w * 4; i++) |
| 608 | dst.f16[i] = float32to16(src.u16[i] / 65535.0f); |
| 609 | } |
| 610 | |
| 611 | static void pasteRGBA16toRGBA32F(Row src, Row dst, int w) |
| 612 | { |
| 613 | for (int i = 0; i < w * 4; i++) |
| 614 | dst.f32[i] = src.u16[i] / 65535.0f; |
| 615 | } |
| 616 | |
| 617 | static void pasteRGBA16FtoRGBA8(Row src, Row dst, int w) |
| 618 | { |
| 619 | for (int i = 0; i < w * 4; i++) |
| 620 | dst.u8[i] = (uint8) (clamp01(float16to32(src.f16[i])) * 255.0f + 0.5f); |
| 621 | } |
| 622 | |
| 623 | static void pasteRGBA16FtoRGBA16(Row src, Row dst, int w) |
| 624 | { |
| 625 | for (int i = 0; i < w * 4; i++) |
| 626 | dst.u16[i] = (uint16) (clamp01(float16to32(src.f16[i])) * 65535.0f + 0.5f); |
| 627 | } |
| 628 | |
| 629 | static void pasteRGBA16FtoRGBA32F(Row src, Row dst, int w) |
| 630 | { |
| 631 | for (int i = 0; i < w * 4; i++) |
| 632 | dst.f32[i] = float16to32(src.f16[i]); |
| 633 | } |
| 634 | |
| 635 | static void pasteRGBA32FtoRGBA8(Row src, Row dst, int w) |
| 636 | { |
| 637 | for (int i = 0; i < w * 4; i++) |
| 638 | dst.u8[i] = (uint8) (clamp01(src.f32[i]) * 255.0f + 0.5f); |
| 639 | } |
| 640 | |
| 641 | static void pasteRGBA32FtoRGBA16(Row src, Row dst, int w) |
| 642 | { |
| 643 | for (int i = 0; i < w * 4; i++) |
| 644 | dst.u16[i] = (uint16) (clamp01(src.f32[i]) * 65535.0f + 0.5f); |
| 645 | } |
| 646 | |
| 647 | static void pasteRGBA32FtoRGBA16F(Row src, Row dst, int w) |
| 648 | { |
| 649 | for (int i = 0; i < w * 4; i++) |
| 650 | dst.f16[i] = float32to16(src.f32[i]); |
| 651 | } |
| 652 | |
| 653 | void ImageData::paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, int sh) |
| 654 | { |
| 655 | PixelFormat dstformat = getFormat(); |
| 656 | PixelFormat srcformat = src->getFormat(); |
| 657 | |
| 658 | int srcW = src->getWidth(); |
| 659 | int srcH = src->getHeight(); |
| 660 | int dstW = getWidth(); |
| 661 | int dstH = getHeight(); |
| 662 | |
| 663 | size_t srcpixelsize = src->getPixelSize(); |
| 664 | size_t dstpixelsize = getPixelSize(); |
| 665 | |
| 666 | // Check bounds; if the data ends up completely out of bounds, get out early. |
| 667 | if (sx >= srcW || sx + sw < 0 || sy >= srcH || sy + sh < 0 |
| 668 | || dx >= dstW || dx + sw < 0 || dy >= dstH || dy + sh < 0) |
| 669 | return; |
| 670 | |
| 671 | // Normalize values to the inside of both images. |
| 672 | if (dx < 0) |
| 673 | { |
| 674 | sw += dx; |
| 675 | sx -= dx; |
| 676 | dx = 0; |
| 677 | } |
| 678 | if (dy < 0) |
| 679 | { |
| 680 | sh += dy; |
| 681 | sy -= dy; |
| 682 | dy = 0; |
| 683 | } |
| 684 | if (sx < 0) |
| 685 | { |
| 686 | sw += sx; |
| 687 | dx -= sx; |
| 688 | sx = 0; |
| 689 | } |
| 690 | if (sy < 0) |
| 691 | { |
| 692 | sh += sy; |
| 693 | dy -= sy; |
| 694 | sy = 0; |
| 695 | } |
| 696 | |
| 697 | if (dx + sw > dstW) |
| 698 | sw = dstW - dx; |
| 699 | |
| 700 | if (dy + sh > dstH) |
| 701 | sh = dstH - dy; |
| 702 | |
| 703 | if (sx + sw > srcW) |
| 704 | sw = srcW - sx; |
| 705 | |
| 706 | if (sy + sh > srcH) |
| 707 | sh = srcH - sy; |
| 708 | |
| 709 | Lock lock2(src->mutex); |
| 710 | Lock lock1(mutex); |
| 711 | |
| 712 | uint8 *s = (uint8 *) src->getData(); |
| 713 | uint8 *d = (uint8 *) getData(); |
| 714 | |
| 715 | auto getfunction = src->pixelGetFunction; |
| 716 | auto setfunction = pixelSetFunction; |
| 717 | |
| 718 | // If the dimensions match up, copy the entire memory stream in one go |
| 719 | if (srcformat == dstformat && (sw == dstW && dstW == srcW && sh == dstH && dstH == srcH)) |
| 720 | { |
| 721 | memcpy(d, s, srcpixelsize * sw * sh); |
| 722 | } |
| 723 | else if (sw > 0) |
| 724 | { |
| 725 | // Otherwise, copy each row individually. |
| 726 | for (int i = 0; i < sh; i++) |
| 727 | { |
| 728 | Row rowsrc = {s + (sx + (i + sy) * srcW) * srcpixelsize}; |
| 729 | Row rowdst = {d + (dx + (i + dy) * dstW) * dstpixelsize}; |
| 730 | |
| 731 | if (srcformat == dstformat) |
| 732 | memcpy(rowdst.u8, rowsrc.u8, srcpixelsize * sw); |
| 733 | |
| 734 | else if (srcformat == PIXELFORMAT_RGBA8 && dstformat == PIXELFORMAT_RGBA16) |
| 735 | pasteRGBA8toRGBA16(rowsrc, rowdst, sw); |
| 736 | else if (srcformat == PIXELFORMAT_RGBA8 && dstformat == PIXELFORMAT_RGBA16F) |
| 737 | pasteRGBA8toRGBA16F(rowsrc, rowdst, sw); |
| 738 | else if (srcformat == PIXELFORMAT_RGBA8 && dstformat == PIXELFORMAT_RGBA32F) |
| 739 | pasteRGBA8toRGBA32F(rowsrc, rowdst, sw); |
| 740 | |
| 741 | else if (srcformat == PIXELFORMAT_RGBA16 && dstformat == PIXELFORMAT_RGBA8) |
| 742 | pasteRGBA16toRGBA8(rowsrc, rowdst, sw); |
| 743 | else if (srcformat == PIXELFORMAT_RGBA16 && dstformat == PIXELFORMAT_RGBA16F) |
| 744 | pasteRGBA16toRGBA16F(rowsrc, rowdst, sw); |
| 745 | else if (srcformat == PIXELFORMAT_RGBA16 && dstformat == PIXELFORMAT_RGBA32F) |
| 746 | pasteRGBA16toRGBA32F(rowsrc, rowdst, sw); |
| 747 | |
| 748 | else if (srcformat == PIXELFORMAT_RGBA16F && dstformat == PIXELFORMAT_RGBA8) |
| 749 | pasteRGBA16FtoRGBA8(rowsrc, rowdst, sw); |
| 750 | else if (srcformat == PIXELFORMAT_RGBA16F && dstformat == PIXELFORMAT_RGBA16) |
| 751 | pasteRGBA16FtoRGBA16(rowsrc, rowdst, sw); |
| 752 | else if (srcformat == PIXELFORMAT_RGBA16F && dstformat == PIXELFORMAT_RGBA32F) |
| 753 | pasteRGBA16FtoRGBA32F(rowsrc, rowdst, sw); |
| 754 | |
| 755 | else if (srcformat == PIXELFORMAT_RGBA32F && dstformat == PIXELFORMAT_RGBA8) |
| 756 | pasteRGBA32FtoRGBA8(rowsrc, rowdst, sw); |
| 757 | else if (srcformat == PIXELFORMAT_RGBA32F && dstformat == PIXELFORMAT_RGBA16) |
| 758 | pasteRGBA32FtoRGBA16(rowsrc, rowdst, sw); |
| 759 | else if (srcformat == PIXELFORMAT_RGBA32F && dstformat == PIXELFORMAT_RGBA16F) |
| 760 | pasteRGBA32FtoRGBA16F(rowsrc, rowdst, sw); |
| 761 | |
| 762 | else |
| 763 | { |
| 764 | // Slow path: convert src -> Colorf -> dst. |
| 765 | Colorf c; |
| 766 | for (int x = 0; x < sw; x++) |
| 767 | { |
| 768 | auto srcp = (const Pixel *) (rowsrc.u8 + x * srcpixelsize); |
| 769 | auto dstp = (Pixel *) (rowdst.u8 + x * dstpixelsize); |
| 770 | getfunction(srcp, c); |
| 771 | setfunction(c, dstp); |
| 772 | } |
| 773 | } |
| 774 | } |
| 775 | } |
| 776 | } |
| 777 | |
| 778 | love::thread::Mutex *ImageData::getMutex() const |
| 779 | { |
| 780 | return mutex; |
| 781 | } |
| 782 | |
| 783 | size_t ImageData::getPixelSize() const |
| 784 | { |
| 785 | return getPixelFormatSize(format); |
| 786 | } |
| 787 | |
| 788 | bool ImageData::validPixelFormat(PixelFormat format) |
| 789 | { |
| 790 | switch (format) |
| 791 | { |
| 792 | case PIXELFORMAT_R8: |
| 793 | case PIXELFORMAT_RG8: |
| 794 | case PIXELFORMAT_RGBA8: |
| 795 | case PIXELFORMAT_R16: |
| 796 | case PIXELFORMAT_RG16: |
| 797 | case PIXELFORMAT_RGBA16: |
| 798 | case PIXELFORMAT_R16F: |
| 799 | case PIXELFORMAT_RG16F: |
| 800 | case PIXELFORMAT_RGBA16F: |
| 801 | case PIXELFORMAT_R32F: |
| 802 | case PIXELFORMAT_RG32F: |
| 803 | case PIXELFORMAT_RGBA32F: |
| 804 | case PIXELFORMAT_RGBA4: |
| 805 | case PIXELFORMAT_RGB5A1: |
| 806 | case PIXELFORMAT_RGB565: |
| 807 | case PIXELFORMAT_RGB10A2: |
| 808 | case PIXELFORMAT_RG11B10F: |
| 809 | return true; |
| 810 | default: |
| 811 | return false; |
| 812 | } |
| 813 | } |
| 814 | |
| 815 | bool ImageData::canPaste(PixelFormat src, PixelFormat dst) |
| 816 | { |
| 817 | if (src == dst) |
| 818 | return true; |
| 819 | |
| 820 | if (!(src == PIXELFORMAT_RGBA8 || src == PIXELFORMAT_RGBA16 |
| 821 | || src == PIXELFORMAT_RGBA16F || src == PIXELFORMAT_RGBA32F)) |
| 822 | return false; |
| 823 | |
| 824 | if (!(dst == PIXELFORMAT_RGBA8 || dst == PIXELFORMAT_RGBA16 |
| 825 | || dst == PIXELFORMAT_RGBA16F || dst == PIXELFORMAT_RGBA32F)) |
| 826 | return false; |
| 827 | |
| 828 | return true; |
| 829 | } |
| 830 | |
| 831 | ImageData::PixelSetFunction ImageData::getPixelSetFunction(PixelFormat format) |
| 832 | { |
| 833 | switch (format) |
| 834 | { |
| 835 | case PIXELFORMAT_R8: return setPixelR8; |
| 836 | case PIXELFORMAT_RG8: return setPixelRG8; |
| 837 | case PIXELFORMAT_RGBA8: return setPixelRGBA8; |
| 838 | case PIXELFORMAT_R16: return setPixelR16; |
| 839 | case PIXELFORMAT_RG16: return setPixelRG16; |
| 840 | case PIXELFORMAT_RGBA16: return setPixelRGBA16; |
| 841 | case PIXELFORMAT_R16F: return setPixelR16F; |
| 842 | case PIXELFORMAT_RG16F: return setPixelRG16F; |
| 843 | case PIXELFORMAT_RGBA16F: return setPixelRGBA16F; |
| 844 | case PIXELFORMAT_R32F: return setPixelR32F; |
| 845 | case PIXELFORMAT_RG32F: return setPixelRG32F; |
| 846 | case PIXELFORMAT_RGBA32F: return setPixelRGBA32F; |
| 847 | case PIXELFORMAT_RGBA4: return setPixelRGBA4; |
| 848 | case PIXELFORMAT_RGB5A1: return setPixelRGB5A1; |
| 849 | case PIXELFORMAT_RGB565: return setPixelRGB565; |
| 850 | case PIXELFORMAT_RGB10A2: return setPixelRGB10A2; |
| 851 | case PIXELFORMAT_RG11B10F: return setPixelRG11B10F; |
| 852 | default: return nullptr; |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | ImageData::PixelGetFunction ImageData::getPixelGetFunction(PixelFormat format) |
| 857 | { |
| 858 | switch (format) |
| 859 | { |
| 860 | case PIXELFORMAT_R8: return getPixelR8; |
| 861 | case PIXELFORMAT_RG8: return getPixelRG8; |
| 862 | case PIXELFORMAT_RGBA8: return getPixelRGBA8; |
| 863 | case PIXELFORMAT_R16: return getPixelR16; |
| 864 | case PIXELFORMAT_RG16: return getPixelRG16; |
| 865 | case PIXELFORMAT_RGBA16: return getPixelRGBA16; |
| 866 | case PIXELFORMAT_R16F: return getPixelR16F; |
| 867 | case PIXELFORMAT_RG16F: return getPixelRG16F; |
| 868 | case PIXELFORMAT_RGBA16F: return getPixelRGBA16F; |
| 869 | case PIXELFORMAT_R32F: return getPixelR32F; |
| 870 | case PIXELFORMAT_RG32F: return getPixelRG32F; |
| 871 | case PIXELFORMAT_RGBA32F: return getPixelRGBA32F; |
| 872 | case PIXELFORMAT_RGBA4: return getPixelRGBA4; |
| 873 | case PIXELFORMAT_RGB5A1: return getPixelRGB5A1; |
| 874 | case PIXELFORMAT_RGB565: return getPixelRGB565; |
| 875 | case PIXELFORMAT_RGB10A2: return getPixelRGB10A2; |
| 876 | case PIXELFORMAT_RG11B10F: return getPixelRG11B10F; |
| 877 | default: return nullptr; |
| 878 | } |
| 879 | } |
| 880 | |
| 881 | bool ImageData::getConstant(const char *in, FormatHandler::EncodedFormat &out) |
| 882 | { |
| 883 | return encodedFormats.find(in, out); |
| 884 | } |
| 885 | |
| 886 | bool ImageData::getConstant(FormatHandler::EncodedFormat in, const char *&out) |
| 887 | { |
| 888 | return encodedFormats.find(in, out); |
| 889 | } |
| 890 | |
| 891 | std::vector<std::string> ImageData::getConstants(FormatHandler::EncodedFormat) |
| 892 | { |
| 893 | return encodedFormats.getNames(); |
| 894 | } |
| 895 | |
| 896 | StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM>::Entry ImageData::encodedFormatEntries[] = |
| 897 | { |
| 898 | {"tga" , FormatHandler::ENCODED_TGA}, |
| 899 | {"png" , FormatHandler::ENCODED_PNG}, |
| 900 | }; |
| 901 | |
| 902 | StringMap<FormatHandler::EncodedFormat, FormatHandler::ENCODED_MAX_ENUM> ImageData::encodedFormats(ImageData::encodedFormatEntries, sizeof(ImageData::encodedFormatEntries)); |
| 903 | |
| 904 | } // image |
| 905 | } // love |
| 906 | |