| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- //--------------------------------------------------------------------------------------
- //CImageSurface
- // Class for storing, manipulating, and copying image data to and from D3D Surfaces
- //
- //--------------------------------------------------------------------------------------
- // (C) 2005 ATI Research, Inc., All rights reserved.
- //--------------------------------------------------------------------------------------
- // Modified from original
- #include "CImageSurface.h"
- namespace ImageProcessingAtom
- {
- //--------------------------------------------------------------------------------------
- // convert D3D 16 bit float to standard 32 bit float
- // Format:
- //
- // 1 sign bit in MSB, (s)
- // 5 bits of biased exponent, (e)
- // 10 bits of fraction, (f), with an additional hidden bit
- // A float16 value, v, made from the format above takes the following meaning:
- //
- // (a) if e == 31 and f != 0, then v is NaN regardless of s
- // (b) if e == 31 and f == 0, then v = (-1)^s * infinity (signed infinity)
- // (c) if 0 < e < 31, then v = (-1)^s * 2^(e-15) * (1.f)
- // (d) if e == 0 and f != 0, then v = (-1)^s * 2^(e-14) * (0.f) (denormalized numbers)
- // (e) if e == 0 and f == 0, then v = (-1)^s *0 (signed zero)
- //
- //--------------------------------------------------------------------------------------
- float CPf16Tof32(uint16 aVal)
- {
- uint32 signVal = (aVal >> 15); //sign bit in MSB
- uint32 exponent = ((aVal >> 10) & 0x01f); //next 5 bits after signbit
- uint32 mantissa = (aVal & 0x03ff); //lower 10 bits
- uint32 rawFloat32Data; //raw binary float data
- //convert s10e5 5-bit exponent to IEEE754 s23e8 8-bit exponent
- if (exponent == 31)
- { // infinity or Nan depending on mantissa
- exponent = 255;
- }
- else if (exponent == 0)
- { // denormalized floats mantissa is treated as = 0.f
- exponent = 0;
- }
- else
- { //change 15base exponent to 127base exponent
- //normalized floats mantissa is treated as = 1.f
- exponent += (127 - 15);
- }
- //convert 10-bit mantissa to 23-bit mantissa
- mantissa <<= (23 - 10);
- //assemble s23e8 number using logical operations
- rawFloat32Data = (signVal << 31) | (exponent << 23) | mantissa;
- //treat raw data as a 32 bit float
- return *((float *)&rawFloat32Data);
- }
- //--------------------------------------------------------------------------------------
- // convert standard 32 bit float to D3D 16 bit float
- //
- // 16-bit float format:
- //
- // 1 sign bit in MSB, (s)
- // 5 bits of biased exponent, (e)
- // 10 bits of fraction, (f), with an additional hidden bit
- // A float16 value, v, made from the format above takes the following meaning:
- //
- // (a) if e == 31 and f != 0, then v is NaN regardless of s
- // (b) if e == 31 and f == 0, then v = (-1)s*infinity (signed infinity)
- // (c) if 0 < e < 31, then v = (-1)s*2(e-15)*(1.f)
- // (d) if e == 0 and f != 0, then v = (-1)s*2(e-14)*(0.f) (denormalized numbers)
- // (e) if e == 0 and f == 0, then v = (-1)s*0 (signed zero)
- //--------------------------------------------------------------------------------------
- uint16 CPf32Tof16(float aVal)
- {
- uint32 rawf32Data = *((uint32 *)&aVal); //raw binary float data
- uint32 signVal = (rawf32Data >> 31); //sign bit in MSB
- uint32 exponent = ((rawf32Data >> 23) & 0xff); //next 8 bits after signbit
- uint32 mantissa = (rawf32Data & 0x7fffff); //mantissa = lower 23 bits
- uint16 rawf16Data;
- //convert IEEE754 s23e8 8-bit exponent to s10e5 5-bit exponent
- if (exponent == 255)
- {//special case 32 bit float is inf or NaN, use mantissa as is
- exponent = 31;
- }
- else if (exponent < ((127 - 15) - 10))
- {//special case, if 32-bit float exponent is out of 16-bit float range, then set 16-bit float to 0
- exponent = 0;
- mantissa = 0;
- }
- else if (exponent >= (127 + (31 - 15)))
- { // max 15based exponent for s10e5 is 31
- // force s10e5 number to represent infinity by setting mantissa to 0
- // and exponent to 31
- exponent = 31;
- mantissa = 0;
- }
- else if (exponent <= (127 - 15))
- { //convert normalized s23e8 float to denormalized s10e5 float
- //add implicit 1.0 to mantissa to convert from 1.f to use as a 0.f mantissa
- mantissa |= (1 << 23);
- //shift over mantissa number of bits equal to exponent underflow
- mantissa = mantissa >> (1 + ((127 - 15) - exponent));
- //zero exponent to treat value as a denormalized number
- exponent = 0;
- }
- else
- { //change 127base exponent to 15base exponent
- // no underflow or overflow of exponent
- //normalized floats mantissa is treated as= 1.f, so
- // no denormalization or exponent derived shifts to the mantissa
- exponent -= (127 - 15);
- }
- //convert 23-bit mantissa to 10-bit mantissa
- mantissa >>= (23 - 10);
- //assemble s10e5 number using logical operations
- rawf16Data = static_cast<uint16>((signVal << 15) | (exponent << 10) | mantissa);
- //return re-assembled raw data as a 32 bit float
- return rawf16Data;
- }
- //--------------------------------------------------------------------------------------
- //size of data types in bytes
- //--------------------------------------------------------------------------------------
- int32 CPTypeSizeOf(int32 a_Type)
- {
- switch (a_Type)
- {
- case CP_VAL_UNORM8:
- case CP_VAL_UNORM8_BGRA:
- return 1;
- break;
- case CP_VAL_UNORM16:
- return 2;
- break;
- case CP_VAL_FLOAT16:
- return 2;
- break;
- case CP_VAL_FLOAT32:
- return 4;
- break;
- default:
- return 1;
- break;
- }
- }
- //--------------------------------------------------------------------------------------
- //get value of data pointed to by a_Ptr given type information
- //--------------------------------------------------------------------------------------
- CP_ITYPE CPTypeGetVal(int32 a_Type, void *a_Ptr)
- {
- switch (a_Type)
- {
- case CP_VAL_UNORM8:
- case CP_VAL_UNORM8_BGRA:
- return (1.0f / 255.0f) * *((uint8 *)a_Ptr);
- break;
- case CP_VAL_UNORM16:
- return (1.0f / 65535.0f) * *((uint16 *)a_Ptr);
- break;
- case CP_VAL_FLOAT16:
- return CPf16Tof32(*((uint16 *)a_Ptr));
- break;
- case CP_VAL_FLOAT32:
- return *((float *)a_Ptr);
- break;
- default:
- return 0;
- break;
- }
- }
- //--------------------------------------------------------------------------------------
- //Given a CP_ITYPE value as input, convert it to the given type specified by a_Type
- // and write the value to a_Ptr
- //--------------------------------------------------------------------------------------
- void CPTypeSetVal(CP_ITYPE a_Val, int32 a_Type, void *a_Ptr)
- {
- CP_ITYPE clampVal; //clamp value to 0-1 range to output UNORM types
- switch (a_Type)
- {
- case CP_VAL_UNORM8:
- case CP_VAL_UNORM8_BGRA:
- clampVal = VM_MIN(VM_MAX(a_Val, 0.0f), 1.0f);
- *((uint8 *)a_Ptr) = (uint8)(clampVal * 255.0f);
- break;
- case CP_VAL_UNORM16:
- clampVal = VM_MIN(VM_MAX(a_Val, 0.0f), 1.0f);
- *((uint16 *)a_Ptr) = (uint16)(clampVal * 65535.0f);
- break;
- case CP_VAL_FLOAT16:
- *((uint16 *)a_Ptr) = CPf32Tof16(a_Val);
- break;
- case CP_VAL_FLOAT32:
- *((float *)a_Ptr) = a_Val;
- break;
- default:
- break;
- }
- }
- //--------------------------------------------------------------------------------------
- //Error handling for imagesurface class
- // Pop up dialog box, and terminate application
- //--------------------------------------------------------------------------------------
- void CImageSurface::FatalError([[maybe_unused]] const WCHAR *a_Msg)
- {
- AZ_Error("Image Processing", false, "CImageSurface Error: %s", a_Msg);
- }
- //--------------------------------------------------------------------------------------
- // Image surface
- //--------------------------------------------------------------------------------------
- CImageSurface::CImageSurface(void)
- {
- m_Width = 0; //cubemap face width
- m_Height = 0; //cubemap face height
- m_NumChannels = 0; //number of channels
- m_ImgData = NULL;
- }
- //--------------------------------------------------------------------------------------
- // Clear
- //--------------------------------------------------------------------------------------
- void CImageSurface::Clear(void)
- {
- m_Width = 0; //cubemap face width
- m_Height = 0; //cubemap face height
- m_NumChannels = 0; //number of channels
- SAFE_DELETE_ARRAY(m_ImgData); //safe delete old image data
- }
- //--------------------------------------------------------------------------------------
- // Initialize surface and associated memory
- //--------------------------------------------------------------------------------------
- void CImageSurface::Init(int32 a_Width, int32 a_Height, int32 a_NumChannels)
- {
- m_Width = a_Width; //cubemap face width
- m_Height = a_Height; //cubemap face height
- m_NumChannels = a_NumChannels; //number of channels
- SAFE_DELETE_ARRAY(m_ImgData); //safe delete old image data
- m_ImgData = new CP_ITYPE[m_Width * m_Height * m_NumChannels]; //assume tight data packing
- if (!m_ImgData)
- {
- FatalError(L"Unable to allocate data for image in CImageSurface::Init.");
- }
- }
- //--------------------------------------------------------------------------------------
- //copy and convert data from external buffer into this surface
- //
- // note that srcPitch == the source pitch in bytes
- //--------------------------------------------------------------------------------------
- void CImageSurface::SetImageData(int32 a_SrcType, int32 a_SrcNumChannels, int32 a_SrcPitch, void *a_SrcDataPtr)
- {
- int32 i, j, k;
- CP_ITYPE *dstDataWalk = m_ImgData;
- uint8 *srcDataWalk = (uint8 *)a_SrcDataPtr;
- int32 srcValueSize = CPTypeSizeOf(a_SrcType);
- int32 srcTexelStep = srcValueSize * a_SrcNumChannels;
- int32 numChannelsSet = VM_MIN(a_SrcNumChannels, m_NumChannels);
- int32 srcChannelSelect;
- //loop over rows
- for (j = 0; j < m_Height; j++)
- {
- //pointer arithmetic to offset pointer by pitch in bytes
- srcDataWalk = ((uint8 *)a_SrcDataPtr + (j * a_SrcPitch));
- //loop over texels within row
- for (i = 0; i < m_Width; i++)
- {
- srcChannelSelect = 0;
- //loop over channels within texel
- for (k = 0; k < numChannelsSet; k++)
- {
- if (a_SrcType == CP_VAL_UNORM8_BGRA) //swap channels 0, and 2 if in BGRA format
- {
- switch (k)
- {
- case 0:
- *(dstDataWalk + 2) = CPTypeGetVal(a_SrcType, srcDataWalk + srcChannelSelect);
- break;
- case 2:
- *(dstDataWalk + 0) = CPTypeGetVal(a_SrcType, srcDataWalk + srcChannelSelect);
- break;
- default:
- *(dstDataWalk + k) = CPTypeGetVal(a_SrcType, srcDataWalk + srcChannelSelect);
- break;
- }
- }
- else
- {
- *(dstDataWalk + k) = CPTypeGetVal(a_SrcType, srcDataWalk + srcChannelSelect);
- }
- srcChannelSelect += srcValueSize;
- }
- dstDataWalk += m_NumChannels;
- srcDataWalk += srcTexelStep;
- }
- }
- }
- //--------------------------------------------------------------------------------------
- // Copy and convert data from external buffer into this surface set image data degamma
- // and scale
- //
- //--------------------------------------------------------------------------------------
- void CImageSurface::SetImageDataClampDegammaScale(int32 a_SrcType, int32 a_SrcNumChannels, int32 a_SrcPitch,
- void *a_SrcDataPtr, float a_MaxClamp, float a_Gamma, float a_Scale)
- {
- int32 i, j, k;
- CP_ITYPE *dstDataWalk = m_ImgData;
- uint8 *srcDataWalk = (uint8 *)a_SrcDataPtr;
- int32 srcValueSize = CPTypeSizeOf(a_SrcType);
- int32 srcTexelStep = srcValueSize * a_SrcNumChannels;
- int32 numChannelsSet = VM_MIN(a_SrcNumChannels, m_NumChannels);
- int32 srcChannelSelect;
- //loop over rows
- for (j = 0; j < m_Height; j++)
- {
- //pointer arithmetic to offset pointer by pitch in bytes
- srcDataWalk = ((uint8 *)a_SrcDataPtr + (j * a_SrcPitch));
- //loop over texels within row
- for (i = 0; i < m_Width; i++)
- {
- srcChannelSelect = 0;
- //loop over channels within texel
- for (k = 0; k < numChannelsSet; k++)
- {
- CP_ITYPE texelVal;
- //get texel value from external buffer
- texelVal = CPTypeGetVal(a_SrcType, srcDataWalk + srcChannelSelect);
- //clamp texelVal using max value only
- // (using texelVal as the min clamping arguement means no minimum clamping)
- VM_CLAMP(texelVal, texelVal, texelVal, a_MaxClamp);
- if (k < 3) //only apply gamma and scale to RGB channels
- {
- //degamma texel val, by raising to the power gamma
- texelVal = static_cast<CP_ITYPE>(pow(texelVal, a_Gamma));
- //scale texel val in linear space (after degamma)
- texelVal *= a_Scale;
- }
- //write data
- if ((a_SrcType == CP_VAL_UNORM8_BGRA) && (k == 0))
- {
- *(dstDataWalk + 2) = texelVal;
- }
- else if ((a_SrcType == CP_VAL_UNORM8_BGRA) && (k == 2))
- {
- *(dstDataWalk + 0) = texelVal;
- }
- else
- {
- *(dstDataWalk + k) = texelVal;
- }
- srcChannelSelect += srcValueSize;
- }
- dstDataWalk += m_NumChannels;
- srcDataWalk += srcTexelStep;
- }
- }
- }
- //--------------------------------------------------------------------------------------
- //copy data from this image surface into an external buffer
- //
- //--------------------------------------------------------------------------------------
- void CImageSurface::GetImageData(int32 a_DstType, int32 a_DstNumChannels, int32 a_DstPitch, void *a_DstDataPtr)
- {
- int32 i, j, k;
- CP_ITYPE *srcDataWalk = m_ImgData;
- uint8 *dstDataWalk = (uint8 *)a_DstDataPtr;
- int32 dstValueSize = CPTypeSizeOf(a_DstType);
- int32 dstTexelStep = dstValueSize * a_DstNumChannels;
- int32 numChannelsSet = VM_MIN(a_DstNumChannels, m_NumChannels);
- int32 dstChannelSelect;
- //loop over rows
- for (j = 0; j < m_Height; j++)
- {
- //pointer arithmetic to offset pointer by pitch in bytes
- dstDataWalk = ((uint8 *)a_DstDataPtr + (j * a_DstPitch));
- //loop over texels within row
- for (i = 0; i < m_Width; i++)
- {
- dstChannelSelect = 0;
- //loop over channels within texel
- for (k = 0; k < numChannelsSet; k++)
- {
- //write data
- if ((a_DstType == CP_VAL_UNORM8_BGRA) && (k == 0))
- {
- CPTypeSetVal(*(srcDataWalk + 2), a_DstType, dstDataWalk + dstChannelSelect);
- }
- else if ((a_DstType == CP_VAL_UNORM8_BGRA) && (k == 2))
- {
- CPTypeSetVal(*(srcDataWalk + 0), a_DstType, dstDataWalk + dstChannelSelect);
- }
- else
- {
- CPTypeSetVal(*(srcDataWalk + k), a_DstType, dstDataWalk + dstChannelSelect);
- }
- dstChannelSelect += dstValueSize;
- }
- srcDataWalk += m_NumChannels;
- dstDataWalk += dstTexelStep;
- }
- }
- }
- //--------------------------------------------------------------------------------------
- // Scale and then apply gamma to image data, then copy image data into an external buffer
- // note: only apply scale and gamma to RGB channels (e.g. first 3 channels)
- //
- //--------------------------------------------------------------------------------------
- void CImageSurface::GetImageDataScaleGamma(int32 a_DstType, int32 a_DstNumChannels, int32 a_DstPitch,
- void *a_DstDataPtr, float a_Scale, float a_Gamma)
- {
- int32 i, j, k;
- CP_ITYPE *srcDataWalk = m_ImgData;
- uint8 *dstDataWalk = (uint8 *)a_DstDataPtr;
- int32 dstValueSize = CPTypeSizeOf(a_DstType);
- int32 dstTexelStep = dstValueSize * a_DstNumChannels;
- int32 numChannelsSet = VM_MIN(a_DstNumChannels, m_NumChannels);
- int32 dstChannelSelect;
- //loop over rows
- for (j = 0; j < m_Height; j++)
- {
- //pointer arithmetic to offset pointer by pitch in bytes
- dstDataWalk = ((uint8 *)a_DstDataPtr + (j * a_DstPitch));
- //loop over texels within row
- for (i = 0; i < m_Width; i++)
- {
- dstChannelSelect = 0;
- //loop over channels within texel
- for (k = 0; k < numChannelsSet; k++)
- {
- CP_ITYPE texelVal;
- texelVal = *(srcDataWalk + k);
- if (k < 3) //only apply gamma and scale to RGB channels
- {
- //scale texel val
- texelVal *= a_Scale;
- //apply gamma to texel val by raising the texelVal to the power of (1/gamma)
- texelVal = static_cast<CP_ITYPE>(pow(texelVal, 1.0f / a_Gamma));
- }
- //write out texture value
- if ((a_DstType == CP_VAL_UNORM8_BGRA) && (k == 0))
- {
- CPTypeSetVal(texelVal, a_DstType, dstDataWalk + (dstValueSize * 2));
- }
- else if ((a_DstType == CP_VAL_UNORM8_BGRA) && (k == 2))
- {
- CPTypeSetVal(texelVal, a_DstType, dstDataWalk + (dstValueSize * 0));
- }
- else
- {
- CPTypeSetVal(texelVal, a_DstType, dstDataWalk + dstChannelSelect);
- }
- dstChannelSelect += dstValueSize;
- }
- srcDataWalk += m_NumChannels;
- dstDataWalk += dstTexelStep;
- }
- }
- }
- //--------------------------------------------------------------------------------------
- //Set image channel a_ChannelIdx to a_ClearColor for all pixels.
- //
- //--------------------------------------------------------------------------------------
- void CImageSurface::ClearChannelConst(int32 a_ChannelIdx, CP_ITYPE a_ClearColor)
- {
- int32 u, v;
- CP_ITYPE *texelPtr;
- //if channel does not exist, do not attempt to clear the channel
- if (a_ChannelIdx > (m_NumChannels - 1))
- {
- return;
- }
- for (v = 0; v < m_Height; v++)
- {
- for (u = 0; u < m_Width; u++)
- {
- texelPtr = GetSurfaceTexelPtr(u, v);
- *(texelPtr + a_ChannelIdx) = a_ClearColor;
- }
- }
- }
- //--------------------------------------------------------------------------------------
- //Gets texel ptr in a surface given u and v coordinates,
- //
- //--------------------------------------------------------------------------------------
- CP_ITYPE *CImageSurface::GetSurfaceTexelPtr(int32 u, int32 v)
- {
- return(m_ImgData + (((m_Width * v) + u) * m_NumChannels));
- }
- //--------------------------------------------------------------------------------------
- //flips surface image in place horizontally
- //
- //--------------------------------------------------------------------------------------
- void CImageSurface::InPlaceHorizonalFlip(void)
- {
- int32 u, v, k;
- CP_ITYPE *texelPtrTop, *texelPtrBottom;
- //iterate over V
- for (v = 0; v < (m_Height / 2); v++)
- {
- for (u = 0; u < m_Height; u++)
- {
- texelPtrTop = GetSurfaceTexelPtr(u, v);
- texelPtrBottom = GetSurfaceTexelPtr(u, (m_Height - 1) - v);
- //iterate over channels
- for (k = 0; k < m_NumChannels; k++)
- {
- CP_ITYPE tmpTexelVal;
- tmpTexelVal = *(texelPtrTop + k);
- *(texelPtrTop + k) = *(texelPtrBottom + k);
- *(texelPtrBottom + k) = tmpTexelVal;
- }
- }
- }
- }
- //--------------------------------------------------------------------------------------
- //flips surface image in place vertically
- //
- //--------------------------------------------------------------------------------------
- void CImageSurface::InPlaceVerticalFlip(void)
- {
- int32 u, v, k;
- CP_ITYPE *texelPtrLeft, *texelPtrRight;
- for (u = 0; u < (m_Width / 2); u++)
- {
- for (v = 0; v < m_Height; v++)
- {
- texelPtrLeft = GetSurfaceTexelPtr(u, v);
- texelPtrRight = GetSurfaceTexelPtr((m_Width - 1) - u, v);
- //iterate over channels
- for (k = 0; k < m_NumChannels; k++)
- {
- CP_ITYPE tmpTexelVal;
- tmpTexelVal = *(texelPtrLeft + k);
- *(texelPtrLeft + k) = *(texelPtrRight + k);
- *(texelPtrRight + k) = tmpTexelVal;
- }
- }
- }
- }
- //--------------------------------------------------------------------------------------
- //flip image around line defined by u = v (effectively swaps the u and v axises)
- //--------------------------------------------------------------------------------------
- void CImageSurface::InPlaceDiagonalUVFlip(void)
- {
- int32 u, v, k;
- CP_ITYPE *texelPtrLeft, *texelPtrRight;
- if (m_Width != m_Height)
- { //only flip image if square
- return;
- }
- for (v = 0; v < m_Height; v++)
- {
- for (u = 0; u < v; u++) //only iterate over lower left triangle
- {
- texelPtrLeft = GetSurfaceTexelPtr(u, v);
- texelPtrRight = GetSurfaceTexelPtr(v, u);
- //iterate over channels
- for (k = 0; k < m_NumChannels; k++)
- {
- CP_ITYPE tmpTexelVal;
- tmpTexelVal = *(texelPtrLeft + k);
- *(texelPtrLeft + k) = *(texelPtrRight + k);
- *(texelPtrRight + k) = tmpTexelVal;
- }
- }
- }
- }
- //--------------------------------------------------------------------------------------
- // destructor, free all memory used
- //--------------------------------------------------------------------------------------
- CImageSurface::~CImageSurface()
- {
- SAFE_DELETE_ARRAY(m_ImgData);
- }
- } // namespace ImageProcessingAtom
|