|
@@ -235,3 +235,248 @@ void SDL_Blit_Slow(SDL_BlitInfo *info)
|
|
|
info->dst += info->dst_pitch;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+static float PQtoNits(float pq)
|
|
|
+{
|
|
|
+ const float c1 = 0.8359375f;
|
|
|
+ const float c2 = 18.8515625f;
|
|
|
+ const float c3 = 18.6875f;
|
|
|
+
|
|
|
+ const float oo_m1 = 1.0f / 0.1593017578125f;
|
|
|
+ const float oo_m2 = 1.0f / 78.84375f;
|
|
|
+
|
|
|
+ float num = SDL_max(SDL_powf(pq, oo_m2) - c1, 0.0f);
|
|
|
+ float den = c2 - c3 * pow(pq, oo_m2);
|
|
|
+
|
|
|
+ return 10000.0f * SDL_powf(num / den, oo_m1);
|
|
|
+}
|
|
|
+
|
|
|
+static void Convert2020to709(float v[3])
|
|
|
+{
|
|
|
+ static const float matrix[3][3] = {
|
|
|
+ { 1.660496f, -0.587656f, -0.072840f },
|
|
|
+ { -0.124547f, 1.132895f, -0.008348f },
|
|
|
+ { -0.018154f, -0.100597f, 1.118751f }
|
|
|
+ };
|
|
|
+
|
|
|
+ float out[3];
|
|
|
+ out[0] = matrix[0][0] * v[0] + matrix[0][1] * v[1] + matrix[0][2] * v[2];
|
|
|
+ out[1] = matrix[1][0] * v[0] + matrix[1][1] * v[1] + matrix[1][2] * v[2];
|
|
|
+ out[2] = matrix[2][0] * v[0] + matrix[2][1] * v[1] + matrix[2][2] * v[2];
|
|
|
+ v[0] = out[0];
|
|
|
+ v[1] = out[1];
|
|
|
+ v[2] = out[2];
|
|
|
+}
|
|
|
+
|
|
|
+/* This isn't really a tone mapping algorithm but it generally works well for HDR -> SDR display */
|
|
|
+static void PQtoSDR(float floatR, float floatG, float floatB, Uint32 *outR, Uint32 *outG, Uint32 *outB)
|
|
|
+{
|
|
|
+ float v[3];
|
|
|
+ int i;
|
|
|
+
|
|
|
+ v[0] = PQtoNits(floatR);
|
|
|
+ v[1] = PQtoNits(floatG);
|
|
|
+ v[2] = PQtoNits(floatB);
|
|
|
+
|
|
|
+ Convert2020to709(v);
|
|
|
+
|
|
|
+ for (i = 0; i < SDL_arraysize(v); ++i) {
|
|
|
+ v[i] /= 400.0f;
|
|
|
+ v[i] = SDL_clamp(v[i], 0.0f, 1.0f);
|
|
|
+ v[i] = SDL_powf(v[i], 1.0f / 2.2f);
|
|
|
+ }
|
|
|
+
|
|
|
+ *outR = (Uint32)(v[0] * 255);
|
|
|
+ *outG = (Uint32)(v[1] * 255);
|
|
|
+ *outB = (Uint32)(v[2] * 255);
|
|
|
+}
|
|
|
+
|
|
|
+void SDL_Blit_Slow_PQtoSDR(SDL_BlitInfo *info)
|
|
|
+{
|
|
|
+ const int flags = info->flags;
|
|
|
+ const Uint32 modulateR = info->r;
|
|
|
+ const Uint32 modulateG = info->g;
|
|
|
+ const Uint32 modulateB = info->b;
|
|
|
+ const Uint32 modulateA = info->a;
|
|
|
+ Uint32 srcpixel;
|
|
|
+ float srcFloatR, srcFloatG, srcFloatB, srcFloatA;
|
|
|
+ Uint32 srcR, srcG, srcB, srcA;
|
|
|
+ Uint32 dstpixel;
|
|
|
+ Uint32 dstR, dstG, dstB, dstA;
|
|
|
+ Uint64 srcy, srcx;
|
|
|
+ Uint64 posy, posx;
|
|
|
+ Uint64 incy, incx;
|
|
|
+ SDL_PixelFormat *src_fmt = info->src_fmt;
|
|
|
+ SDL_PixelFormat *dst_fmt = info->dst_fmt;
|
|
|
+ int srcbpp = src_fmt->BytesPerPixel;
|
|
|
+ int dstbpp = dst_fmt->BytesPerPixel;
|
|
|
+ int dstfmt_val;
|
|
|
+ Uint32 rgbmask = ~src_fmt->Amask;
|
|
|
+ Uint32 ckey = info->colorkey & rgbmask;
|
|
|
+
|
|
|
+ dstfmt_val = detect_format(dst_fmt);
|
|
|
+
|
|
|
+ incy = ((Uint64)info->src_h << 16) / info->dst_h;
|
|
|
+ incx = ((Uint64)info->src_w << 16) / info->dst_w;
|
|
|
+ posy = incy / 2; /* start at the middle of pixel */
|
|
|
+
|
|
|
+ while (info->dst_h--) {
|
|
|
+ Uint8 *src = 0;
|
|
|
+ Uint8 *dst = info->dst;
|
|
|
+ int n = info->dst_w;
|
|
|
+ posx = incx / 2; /* start at the middle of pixel */
|
|
|
+ srcy = posy >> 16;
|
|
|
+ while (n--) {
|
|
|
+ srcx = posx >> 16;
|
|
|
+ src = (info->src + (srcy * info->src_pitch) + (srcx * srcbpp));
|
|
|
+
|
|
|
+ /* 10-bit pixel format */
|
|
|
+ srcpixel = *((Uint32 *)(src));
|
|
|
+ switch (src_fmt->format) {
|
|
|
+ case SDL_PIXELFORMAT_XRGB2101010:
|
|
|
+ RGBAFLOAT_FROM_ARGB2101010(srcpixel, srcFloatR, srcFloatG, srcFloatB, srcFloatA);
|
|
|
+ srcFloatA = 1.0f;
|
|
|
+ break;
|
|
|
+ case SDL_PIXELFORMAT_XBGR2101010:
|
|
|
+ RGBAFLOAT_FROM_ABGR2101010(srcpixel, srcFloatR, srcFloatG, srcFloatB, srcFloatA);
|
|
|
+ srcFloatA = 1.0f;
|
|
|
+ break;
|
|
|
+ case SDL_PIXELFORMAT_ARGB2101010:
|
|
|
+ RGBAFLOAT_FROM_ARGB2101010(srcpixel, srcFloatR, srcFloatG, srcFloatB, srcFloatA);
|
|
|
+ break;
|
|
|
+ case SDL_PIXELFORMAT_ABGR2101010:
|
|
|
+ RGBAFLOAT_FROM_ABGR2101010(srcpixel, srcFloatR, srcFloatG, srcFloatB, srcFloatA);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* Unsupported pixel format */
|
|
|
+ srcFloatR = srcFloatG = srcFloatB = srcFloatA = 0.0f;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ PQtoSDR(srcFloatR, srcFloatG, srcFloatB, &srcR, &srcG, &srcB);
|
|
|
+ srcA = (Uint32)(srcFloatA * 255);
|
|
|
+
|
|
|
+ if (flags & SDL_COPY_COLORKEY) {
|
|
|
+ /* srcpixel isn't set for 24 bpp */
|
|
|
+ if (srcbpp == 3) {
|
|
|
+ srcpixel = (srcR << src_fmt->Rshift) |
|
|
|
+ (srcG << src_fmt->Gshift) | (srcB << src_fmt->Bshift);
|
|
|
+ }
|
|
|
+ if ((srcpixel & rgbmask) == ckey) {
|
|
|
+ posx += incx;
|
|
|
+ dst += dstbpp;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL))) {
|
|
|
+ if (FORMAT_HAS_ALPHA(dstfmt_val)) {
|
|
|
+ DISEMBLE_RGBA(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB, dstA);
|
|
|
+ } else if (FORMAT_HAS_NO_ALPHA(dstfmt_val)) {
|
|
|
+ DISEMBLE_RGB(dst, dstbpp, dst_fmt, dstpixel, dstR, dstG, dstB);
|
|
|
+ dstA = 0xFF;
|
|
|
+ } else {
|
|
|
+ /* SDL_PIXELFORMAT_ARGB2101010 */
|
|
|
+ dstpixel = *((Uint32 *)(dst));
|
|
|
+ RGBA_FROM_ARGB2101010(dstpixel, dstR, dstG, dstB, dstA);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* don't care */
|
|
|
+ dstR = dstG = dstB = dstA = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (flags & SDL_COPY_MODULATE_COLOR) {
|
|
|
+ srcR = (srcR * modulateR) / 255;
|
|
|
+ srcG = (srcG * modulateG) / 255;
|
|
|
+ srcB = (srcB * modulateB) / 255;
|
|
|
+ }
|
|
|
+ if (flags & SDL_COPY_MODULATE_ALPHA) {
|
|
|
+ srcA = (srcA * modulateA) / 255;
|
|
|
+ }
|
|
|
+ if (flags & (SDL_COPY_BLEND | SDL_COPY_ADD)) {
|
|
|
+ /* This goes away if we ever use premultiplied alpha */
|
|
|
+ if (srcA < 255) {
|
|
|
+ srcR = (srcR * srcA) / 255;
|
|
|
+ srcG = (srcG * srcA) / 255;
|
|
|
+ srcB = (srcB * srcA) / 255;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ switch (flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL)) {
|
|
|
+ case 0:
|
|
|
+ dstR = srcR;
|
|
|
+ dstG = srcG;
|
|
|
+ dstB = srcB;
|
|
|
+ dstA = srcA;
|
|
|
+ break;
|
|
|
+ case SDL_COPY_BLEND:
|
|
|
+ dstR = srcR + ((255 - srcA) * dstR) / 255;
|
|
|
+ dstG = srcG + ((255 - srcA) * dstG) / 255;
|
|
|
+ dstB = srcB + ((255 - srcA) * dstB) / 255;
|
|
|
+ dstA = srcA + ((255 - srcA) * dstA) / 255;
|
|
|
+ break;
|
|
|
+ case SDL_COPY_ADD:
|
|
|
+ dstR = srcR + dstR;
|
|
|
+ if (dstR > 255) {
|
|
|
+ dstR = 255;
|
|
|
+ }
|
|
|
+ dstG = srcG + dstG;
|
|
|
+ if (dstG > 255) {
|
|
|
+ dstG = 255;
|
|
|
+ }
|
|
|
+ dstB = srcB + dstB;
|
|
|
+ if (dstB > 255) {
|
|
|
+ dstB = 255;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SDL_COPY_MOD:
|
|
|
+ dstR = (srcR * dstR) / 255;
|
|
|
+ dstG = (srcG * dstG) / 255;
|
|
|
+ dstB = (srcB * dstB) / 255;
|
|
|
+ break;
|
|
|
+ case SDL_COPY_MUL:
|
|
|
+ dstR = ((srcR * dstR) + (dstR * (255 - srcA))) / 255;
|
|
|
+ if (dstR > 255) {
|
|
|
+ dstR = 255;
|
|
|
+ }
|
|
|
+ dstG = ((srcG * dstG) + (dstG * (255 - srcA))) / 255;
|
|
|
+ if (dstG > 255) {
|
|
|
+ dstG = 255;
|
|
|
+ }
|
|
|
+ dstB = ((srcB * dstB) + (dstB * (255 - srcA))) / 255;
|
|
|
+ if (dstB > 255) {
|
|
|
+ dstB = 255;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (FORMAT_HAS_ALPHA(dstfmt_val)) {
|
|
|
+ ASSEMBLE_RGBA(dst, dstbpp, dst_fmt, dstR, dstG, dstB, dstA);
|
|
|
+ } else if (FORMAT_HAS_NO_ALPHA(dstfmt_val)) {
|
|
|
+ ASSEMBLE_RGB(dst, dstbpp, dst_fmt, dstR, dstG, dstB);
|
|
|
+ } else {
|
|
|
+ /* 10-bit pixel format */
|
|
|
+ Uint32 pixel;
|
|
|
+ switch (dst_fmt->format) {
|
|
|
+ case SDL_PIXELFORMAT_XRGB2101010:
|
|
|
+ dstA = 0xFF;
|
|
|
+ SDL_FALLTHROUGH;
|
|
|
+ case SDL_PIXELFORMAT_ARGB2101010:
|
|
|
+ ARGB2101010_FROM_RGBA(pixel, dstR, dstG, dstB, dstA);
|
|
|
+ break;
|
|
|
+ case SDL_PIXELFORMAT_XBGR2101010:
|
|
|
+ dstA = 0xFF;
|
|
|
+ SDL_FALLTHROUGH;
|
|
|
+ case SDL_PIXELFORMAT_ABGR2101010:
|
|
|
+ ABGR2101010_FROM_RGBA(pixel, dstR, dstG, dstB, dstA);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ pixel = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ *(Uint32 *)dst = pixel;
|
|
|
+ }
|
|
|
+ posx += incx;
|
|
|
+ dst += dstbpp;
|
|
|
+ }
|
|
|
+ posy += incy;
|
|
|
+ info->dst += info->dst_pitch;
|
|
|
+ }
|
|
|
+}
|