|
19 | 19 | #include <algorithm> |
20 | 20 | #include <cstring> |
21 | 21 | #include <cstdlib> |
| 22 | +#include <memory> |
22 | 23 |
|
23 | 24 | #include "Debug/plDebug.h" |
24 | 25 | #include "Util/plJPEG.h" |
25 | 26 | #include "Util/plPNG.h" |
26 | 27 | #include "Util/plDDSurface.h" |
| 28 | +#include "Stream/hsNullStream.h" |
27 | 29 | #include "Stream/hsRAMStream.h" |
28 | 30 | #include "3rdPartyLibs/squish/squish.h" |
29 | 31 |
|
@@ -771,6 +773,75 @@ void plMipmap::CompressImage(size_t level, void* src, size_t size, BlockQuality |
771 | 773 | } |
772 | 774 | } |
773 | 775 |
|
| 776 | +void plMipmap::CompressJPEG(int quality, bool force) |
| 777 | +{ |
| 778 | + if (fCompressionType != kJPEGCompression) |
| 779 | + throw hsBadParamException(__FILE__, __LINE__, "JPEG compression only supported on JPEG mipmaps"); |
| 780 | + |
| 781 | + // We don't want to recompress if we already have JPEG data. |
| 782 | + // That can cause data loss, artificating, and diff churn. |
| 783 | + // But, if they say force... go for it. |
| 784 | + bool compressColor = fJPEGCache.empty() || force; |
| 785 | + bool compressAlpha = fJAlphaCache.empty() || force; |
| 786 | + |
| 787 | + if (!force) { |
| 788 | + { |
| 789 | + hsNullStream S; |
| 790 | + IWriteRLEImage(&S, false); |
| 791 | + compressColor = S.size() >= 5 * 1024; |
| 792 | + } |
| 793 | + { |
| 794 | + hsNullStream S; |
| 795 | + IWriteRLEImage(&S, true); |
| 796 | + compressAlpha = S.size() >= 5 * 1024; |
| 797 | + } |
| 798 | + } |
| 799 | + |
| 800 | + if (compressColor) { |
| 801 | + auto colorBuf = std::make_unique <uint8_t[]>(fWidth * fHeight * 3); |
| 802 | + |
| 803 | + // Guess what? plJPEG::CompressImage() expects RGB, not BGR. |
| 804 | + // Just roll our own conversion loop here. |
| 805 | + const unsigned char* sp = (const unsigned char*)fImageData; |
| 806 | + unsigned char* dp = (unsigned char*)colorBuf.get(); |
| 807 | + for (size_t i = 0; i < fLevelData[0].fSize; i += 4) { |
| 808 | + unsigned char b = *sp++; |
| 809 | + unsigned char g = *sp++; |
| 810 | + unsigned char r = *sp++; |
| 811 | + sp++; // Skip alpha |
| 812 | + *dp++ = r; |
| 813 | + *dp++ = g; |
| 814 | + *dp++ = b; |
| 815 | + } |
| 816 | + |
| 817 | + hsRAMStream S; |
| 818 | + plJPEG::CompressJPEG(&S, colorBuf.get(), fWidth * fHeight * 3, fWidth, fHeight, 24, quality); |
| 819 | + fJPEGCache.resize(S.size()); |
| 820 | + memcpy(fJPEGCache.data(), S.data(), S.size()); |
| 821 | + } |
| 822 | + |
| 823 | + if (compressAlpha) { |
| 824 | + auto alphaBuf = std::make_unique <uint8_t[]>(fWidth * fHeight * 3); |
| 825 | + |
| 826 | + // extractAlphaData only extracts 1 byte per pixel, but plJPEG expects 3 bytes per pixel. |
| 827 | + // Cyan's alpha JPEGs stuff the alpha byte into the red channel only. |
| 828 | + const unsigned char* sp = (const unsigned char*)fImageData; |
| 829 | + unsigned char* dp = (unsigned char*)alphaBuf.get(); |
| 830 | + for (size_t i = 0; i < fLevelData[0].fSize; i += 4) { |
| 831 | + sp += 3; // Skip RGB |
| 832 | + *dp++ = *sp++; // Alpha (R) |
| 833 | + *dp++ = 0; // G |
| 834 | + *dp++ = 0; // B |
| 835 | + } |
| 836 | + |
| 837 | + hsRAMStream S; |
| 838 | + // Cyan's alpha JPEGs are always 100% quality. |
| 839 | + plJPEG::CompressJPEG(&S, alphaBuf.get(), fWidth * fHeight * 3, fWidth, fHeight, 24, 100); |
| 840 | + fJAlphaCache.resize(S.size()); |
| 841 | + memcpy(fJAlphaCache.data(), S.data(), S.size()); |
| 842 | + } |
| 843 | +} |
| 844 | + |
774 | 845 | /* plLODMipmap */ |
775 | 846 | void plLODMipmap::read(hsStream* S, plResManager* mgr) |
776 | 847 | { |
|
0 commit comments