diff foosdk/sdk/libPPUI/ImageEncoder.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/sdk/libPPUI/ImageEncoder.cpp	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,157 @@
+#include "stdafx.h"
+#include "ImageEncoder.h"
+#include "gdiplus_helpers.h"
+#include <memory>
+#include <vector>
+
+using namespace Gdiplus;
+static GdiplusErrorHandler EH;
+
+int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
+{
+   UINT  num = 0;          // number of image encoders
+   UINT  size = 0;         // size of the image encoder array in bytes
+
+   ImageCodecInfo* pImageCodecInfo = NULL;
+
+   GetImageEncodersSize(&num, &size);
+   if(size == 0)
+      return -1;  // Failure
+
+   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
+   if(pImageCodecInfo == NULL)
+      return -1;  // Failure
+
+   GetImageEncoders(num, size, pImageCodecInfo);
+
+   for(UINT j = 0; j < num; ++j)
+   {
+      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
+      {
+         *pClsid = pImageCodecInfo[j].Clsid;
+         free(pImageCodecInfo);
+         return j;  // Success
+      }    
+   }
+
+   free(pImageCodecInfo);
+   return -1;  // Failure
+}
+
+void ConvertImage(const TCHAR * in, const TCHAR * out, const TCHAR * format, ULONG quality) {
+	GdiplusScope scope;
+	Bitmap image (in);
+	EH << image.GetLastStatus();
+	SaveImage(&image, out, format, quality);
+}
+
+static void add16clip(uint16_t& v, int d) {
+	int v2 = (int) v + d;
+	if ( v2 < 0 ) v2 = 0;
+	if ( v2 > (int) UINT16_MAX ) v2 = UINT16_MAX;
+	v = (uint16_t) v2;
+}
+
+std::unique_ptr<Gdiplus::Bitmap> image16bpcto8(Gdiplus::Bitmap* img) {
+
+	const unsigned channels = [img] {
+		switch (img->GetPixelFormat()) {
+		case PixelFormat48bppRGB:
+			return 3;
+		case PixelFormat16bppGrayScale:
+			return 1;
+		default:
+			throw std::runtime_error("Invalid pixel format");
+		}
+	} ();
+
+	
+	const unsigned Width = img->GetWidth();
+	const unsigned Height = img->GetHeight();
+
+	if ( Width == 0 || Height == 0 ) throw std::runtime_error("Invalid dimensions");
+
+	std::unique_ptr< Gdiplus::Bitmap > ret ( new Gdiplus::Bitmap( Width, Height, channels == 3 ? PixelFormat24bppRGB : PixelFormat8bppIndexed ) );
+
+	
+	Gdiplus::Rect rcWhole = {};
+	rcWhole.Width = Width; rcWhole.Height = Height;
+	Gdiplus::BitmapData dataIn, dataOut;
+	EH << ret->LockBits( &rcWhole, ImageLockModeWrite, PixelFormat24bppRGB, &dataOut);
+	EH << img->LockBits(&rcWhole, ImageLockModeRead, PixelFormat48bppRGB, &dataIn);
+
+	
+	
+
+	const uint8_t * inLine = (const uint8_t*)dataIn.Scan0;
+	uint8_t * outLine = (uint8_t*)dataOut.Scan0;
+
+	std::vector<uint16_t> curLineBuf, nextLineBuf;
+	curLineBuf.resize( (Width + 2) * channels );
+	nextLineBuf.resize( (Width + 2) * channels );
+
+	memcpy(&nextLineBuf[channels], inLine, Width * channels * 2 );
+
+	for (unsigned y = 0; y < Height; ++y) {
+		std::swap( curLineBuf, nextLineBuf );
+		if (y + 1 < Height) {
+			inLine += dataIn.Stride;
+			memcpy(&nextLineBuf[channels], inLine, Width * channels * 2 );
+		}
+
+		auto & inPix = curLineBuf;
+		auto & inPixNext = nextLineBuf;
+		size_t inOffset = channels;
+		uint8_t * outPix = outLine;
+		for (unsigned x = 0; x < Width; ++x) {
+			for (int c = 0; c < (int)channels; ++c) {
+				uint16_t orig = inPix[inOffset];
+				uint16_t v8 = orig >> 8;
+				uint16_t v16 = v8 | (v8 << 8);
+
+				outPix[c] = (uint8_t) v8;
+
+				int d = (int) orig - (int) v16;
+
+				add16clip(inPix    [inOffset + channels], d * 7 / 16 ); // x+1
+				add16clip(inPixNext[inOffset - channels], d * 3 / 16 ); // x-1, y+1
+				add16clip(inPixNext[inOffset           ], d * 5 / 16 ); // y+1
+				add16clip(inPixNext[inOffset + channels], d * 1 / 16 ); // x+1, y+1
+
+				++ inOffset;
+			}
+
+			outPix += channels;
+		}
+		
+		outLine += dataOut.Stride;
+	}
+
+	EH << img->UnlockBits(&dataIn);
+	EH << ret->UnlockBits(&dataOut);
+
+	return ret;
+}
+
+void SaveImage(Gdiplus::Image* bmp, const TCHAR* out, const TCHAR* format, ULONG quality) {
+	if (bmp->GetType() != Gdiplus::ImageTypeBitmap) throw std::runtime_error("Bitmap expected");
+	SaveImage(static_cast<Gdiplus::Bitmap*>(bmp), out, format, quality);
+}
+void SaveImage(Gdiplus::Bitmap* image, const TCHAR* out, const TCHAR* format, ULONG quality) {
+	CLSID             encoderClsid;
+	EncoderParameters encoderParameters;
+
+	if (GetEncoderClsid(format, &encoderClsid) < 0) throw std::runtime_error("Encoder not found");
+
+	if (_tcscmp(format, _T("image/jpeg")) == 0) {
+		encoderParameters.Count = 1;
+		encoderParameters.Parameter[0].Guid = EncoderQuality;
+		encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
+		encoderParameters.Parameter[0].NumberOfValues = 1;
+		encoderParameters.Parameter[0].Value = &quality;
+
+		EH << image->Save(out, &encoderClsid, &encoderParameters);
+	} else {
+		EH << image->Save(out, &encoderClsid, NULL);
+	}
+}