summaryrefslogtreecommitdiff
path: root/Runtime/Graphics/Texture2D.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Graphics/Texture2D.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Graphics/Texture2D.cpp')
-rw-r--r--Runtime/Graphics/Texture2D.cpp1422
1 files changed, 1422 insertions, 0 deletions
diff --git a/Runtime/Graphics/Texture2D.cpp b/Runtime/Graphics/Texture2D.cpp
new file mode 100644
index 0000000..691dfd9
--- /dev/null
+++ b/Runtime/Graphics/Texture2D.cpp
@@ -0,0 +1,1422 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Texture2D.h"
+#include "Image.h"
+#include "RenderTexture.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "S3Decompression.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Serialize/SwapEndianArray.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include "ImageConversion.h"
+#include "Runtime/Misc/Allocator.h"
+#include "DXTCompression.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Misc/BuildSettings.h"
+
+#if UNITY_EDITOR
+#include "CubemapTexture.h"
+#endif
+
+#if UNITY_WII
+#include "PlatformDependent/wii/WiiUtility.h"
+#endif
+
+#if ENABLE_TEXTUREID_MAP
+#include "Runtime/GfxDevice/TextureIdMap.h"
+#endif
+
+
+#define USE_IMMEDIATE_INTEGRATION (UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_PS3 || UNITY_XENON || UNITY_TIZEN)
+
+
+PROFILER_INFORMATION(gIntegrateLoadedImmediately, "Texture.IntegrateLoadedImmediately", kProfilerLoading)
+PROFILER_INFORMATION(gAwakeFromLoadTex2D, "Texture.AwakeFromLoad", kProfilerLoading)
+
+/*
+ Regular and non-power-of-two textures:
+
+ For regular textures, all TextureRepresentation's are exactly the same; and the data is allocated just
+ once (all representations point to the same data).
+
+ For NPOT textures:
+ * m_TexData is what is serialized (non power of two). Mip maps use floor(size/2) convention,
+ DXT compression is actually next multiple of 4.
+ * If no mipmaps are specified and NPOTRestricted is supported - act as regular texture
+ * If mipmaps are specified and NPOTFULL is supported - act as regular texture
+ * If NPOT textures are not supported:
+ * m_Tex is scaled up to POT at load time. DXT source gets decompressed into ARGB32.
+ * m_TexPadded is POT size, but the original is padded by repeating border pixels. GUITexture uses it.
+*/
+
+bool Texture2D::s_ScreenReadAllowed = true;
+
+static inline UInt32 GetBytesForOnePixel( TextureFormat format )
+{
+ return IsAnyCompressedTextureFormat(format) ? 0 : GetBytesFromTextureFormat(format);
+}
+
+bool IsNPOTTextureAllowed(bool hasMipMap)
+{
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+ return hasMipMap ? gGraphicsCaps.npot == kNPOTFull : gGraphicsCaps.npot >= kNPOTRestricted;
+ else
+ return false;
+}
+
+static int GetNextAllowedTextureSize(int size, bool hasMipMap, TextureFormat format)
+{
+ int multipleMask = GetTextureSizeAllowedMultiple(format) - 1;
+ size = (size + multipleMask) & ~multipleMask;
+ if (!IsNPOTTextureAllowed(hasMipMap))
+ size = NextPowerOfTwo(size);
+ return size;
+}
+
+
+Texture2D::TextureRepresentation::TextureRepresentation()
+: data(NULL)
+, width(0)
+, height(0)
+, format(kTexFormatARGB32)
+, imageSize(0)
+{
+}
+
+Texture2D::Texture2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_InitFlags(0)
+{
+ #if UNITY_EDITOR
+ m_IgnoreMasterTextureLimit = false;
+ #endif
+
+ m_MipMap = false;
+ m_TextureDimension = kTexDimNone;
+
+#if UNITY_EDITOR
+ m_EditorDontWriteTextureData = false;
+ m_AlphaIsTransparency = false;
+#endif
+
+ m_PowerOfTwo = true;
+
+ m_ImageCount = 0;
+
+ m_IsReadable = true;
+ m_IsUnreloadable = false;
+ m_ReadAllowed = true;
+ m_TextureUploaded = false;
+ m_UnscaledTextureUploaded = false;
+
+ // We use unchecked version since we may not be on the main thread
+ // This means CreateTextureID() implementation must be thread safe!
+ m_UnscaledTexID = GetUncheckedGfxDevice().CreateTextureID();
+}
+
+void Texture2D::Reset ()
+{
+ Super::Reset();
+
+ m_TextureSettings.Reset ();
+}
+
+//#define LOG_AWAKE(x, arg) printf_console(x, arg);
+#define LOG_AWAKE(x, arg)
+
+typedef std::vector<Texture2D*> TexturesT;
+static TexturesT g_TexturesToUploadOnMainThread;
+static Mutex g_UploadMutex;
+
+void Texture2D::AwakeFromLoadThreaded()
+{
+ Super::AwakeFromLoadThreaded();
+
+#if USE_IMMEDIATE_INTEGRATION
+ LOG_AWAKE("Texture2D: AwakeFromLoadThreaded (%s)\n", GetName());
+ g_UploadMutex.Lock();
+ g_TexturesToUploadOnMainThread.push_back(this);
+ g_UploadMutex.Unlock();
+ // Pause loading for a while
+ // this will allow main thread to start integrating previously loaded textures
+ Thread::Sleep(0.001);
+#endif // USE_IMMEDIATE_INTEGRATION
+}
+
+// Helps to avoid memory peak while loading the level on platforms where graphics driver creates an internal copy of texture data
+//
+// Memory peak arrises in the following scenario:
+// 1) [Load thread] all texture assets are loaded
+// 2) [Main thread] for every asset (OnAwake)
+// a) texture data is uploaded to graphics driver (copy)
+// b) texture data is released (in case of NonReadable textures)
+//
+// Instead we do:
+// 1) [Load thread] load 1 texture asset
+// 2) [Load thread] stall for a moment and allow Main thread to start uploading
+// 3) [Main thread] upload as much texture data as possible
+// 4) [Main thread] if significantly behind Load thread, then block Load thread
+// 5) continue for all assets
+//
+// NOTE: Main thread calls IntegrateLoadedImmediately() from PreloadManager::UpdatePreloadingSingleStep()
+void Texture2D::IntegrateLoadedImmediately()
+{
+#if USE_IMMEDIATE_INTEGRATION
+
+ PROFILER_AUTO(gIntegrateLoadedImmediately, NULL)
+
+ g_UploadMutex.Lock();
+ TexturesT toUpload = g_TexturesToUploadOnMainThread;
+ g_TexturesToUploadOnMainThread.clear();
+
+ size_t totalTextureSize = 0;
+ for (size_t q = 0; q < toUpload.size(); ++q)
+ totalTextureSize += toUpload[q]->m_TexData.imageSize;
+
+ // Evalute texture data ready for immediate integration
+ // If we have too much data to upload, then block the loading thread!
+ // Loading thread will stall until all pending data is uploaded.
+ bool blockLoadingThread = totalTextureSize > (512*1024);
+
+ // Avoid stalling on every texture (if possible) to improve loading speed
+ if (!blockLoadingThread)
+ {
+ g_UploadMutex.Unlock();
+ }
+ else
+ {
+ LOG_AWAKE("Texture2D: IntegrateLoadedImmediately() will block loader thread, pending texture data size: %d bytes\n", totalTextureSize);
+ }
+
+ for (size_t q = 0; q < toUpload.size(); ++q)
+ {
+ Texture2D* tex = toUpload[q];
+ LOG_AWAKE("Texture2D: Immediately upload texture data (%s)\n", tex->GetName());
+ if (tex->m_TexData.data != NULL)
+ tex->UploadTexture (false);
+ }
+
+ if (blockLoadingThread)
+ g_UploadMutex.Unlock();
+
+#endif // USE_IMMEDIATE_INTEGRATION
+}
+
+void Texture2D::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ SET_ALLOC_OWNER(this);
+ Super::AwakeFromLoad (awakeMode);
+
+ if( (awakeMode == kDefaultAwakeFromLoad || awakeMode == kInstantiateOrCreateFromCodeAwakeFromLoad)
+ && m_TexData.data == NULL
+ )
+ {
+ // actually pretty valid
+ return;
+ }
+
+#if USE_IMMEDIATE_INTEGRATION
+ // Check if texture data was already uploaded by IntegrateLoadedImmediately()
+ if ((awakeMode & kDidLoadThreaded) != 0)
+ {
+ bool dynamicTexture = m_TexData.imageSize == 0;
+ Assert(m_TextureUploaded || dynamicTexture);
+
+ if (m_TextureUploaded)
+ return;
+ }
+ LOG_AWAKE("Texture2D: Upload texture data in Awake (%s)\n", GetName());
+#endif // USE_IMMEDIATE_INTEGRATION
+
+ PROFILER_AUTO(gAwakeFromLoadTex2D, this)
+ UploadTexture (false);
+}
+
+Texture2D::~Texture2D ()
+{
+ DestroyTexture ();
+ MainThreadCleanup ();
+}
+
+bool Texture2D::MainThreadCleanup ()
+{
+ DeleteGfxTexture ();
+
+ Texture::s_TextureIDMap.erase (m_UnscaledTexID);
+
+ // FreeTextureID() implementation must be thread safe!
+ GetUncheckedGfxDevice().FreeTextureID(m_UnscaledTexID);
+ m_UnscaledTexID = TextureID();
+
+ return Super::MainThreadCleanup ();
+}
+
+
+static int SourceMipLevelForBlit( int srcWidth, int srcHeight, int dstWidth, int dstHeight )
+{
+ int level = HighestBit( NextPowerOfTwo(srcWidth) ) - HighestBit( NextPowerOfTwo(dstWidth) );
+ level = std::max( level, HighestBit( NextPowerOfTwo(srcHeight) ) - HighestBit( NextPowerOfTwo(dstHeight) ) );
+ level = std::max( 0, level );
+ return level;
+}
+
+// Compressed->compressed extraction. Copies compressed blocks and pads the remainder with
+// transparent black.
+void Texture2D::ExtractCompressedImageInternal( UInt8* dst, int dstWidth, int dstHeight, int imageIndex ) const
+{
+ AssertIf( imageIndex < 0 || imageIndex >= m_ImageCount );
+
+ TextureRepresentation const& rep =m_TexData;
+ AssertIf( !IsAnyCompressedTextureFormat( rep.format ) );
+ if (m_TexData.data == NULL)
+ {
+ ErrorString ("Texture data can not be accessed");
+ return;
+ }
+
+ int mipmapLevel = SourceMipLevelForBlit( rep.width, rep.height, dstWidth, dstHeight );
+ mipmapLevel = std::min( mipmapLevel, CountDataMipmaps() - 1 );
+ int mipmapoffset = CalculateMipMapOffset( rep.width, rep.height, rep.format, mipmapLevel );
+ int width = std::max( rep.width >> mipmapLevel, 1 );
+ int height = std::max( rep.height >> mipmapLevel, 1 );
+
+ // pad-copy compressed->compressed
+ BlitCopyCompressedImage( rep.format, rep.data + imageIndex * rep.imageSize + mipmapoffset,
+ width, height, dst, dstWidth, dstHeight, true );
+}
+
+
+bool Texture2D::ExtractImageInternal( ImageReference* image, bool scaleToSize, int imageIndex ) const
+{
+ AssertIf (imageIndex < 0 || imageIndex >= m_ImageCount);
+
+ TextureRepresentation const& rep = m_TexData;
+ if(rep.data == NULL)
+ {
+ ErrorString ("Texture is not accessible.");
+ return false;
+ }
+
+ int mipmapLevel = SourceMipLevelForBlit( rep.width, rep.height, image->GetWidth(), image->GetHeight() );
+ mipmapLevel = std::min( mipmapLevel, CountDataMipmaps() - 1 );
+ int mipmapoffset = CalculateMipMapOffset( rep.width, rep.height, rep.format, mipmapLevel );
+ int width = std::max( rep.width >> mipmapLevel, 1 );
+ int height = std::max( rep.height >> mipmapLevel, 1 );
+
+ if( IsAnyCompressedTextureFormat(rep.format) )
+ {
+ // Decompress into upper multiple-of-4 size
+ int decompWidth = (width + 3) / 4 * 4;
+ int decompHeight = (height + 3) / 4 * 4;
+
+ Image decompressed( decompWidth, decompHeight, kTexFormatRGBA32 );
+ UInt8* compressed = rep.data + imageIndex * rep.imageSize + mipmapoffset;
+
+ if (!DecompressNativeTextureFormatWithMipLevel( rep.format, width, height, mipmapLevel, (UInt32*)compressed, decompWidth, decompHeight, (UInt32*)decompressed.GetImageData() ))
+ return false;
+
+ // Clip this back to original size
+ ImageReference clipped = decompressed.ClipImage( 0, 0, width, height );
+
+ if( scaleToSize )
+ {
+ image->BlitImage( clipped, ImageReference::BLIT_BILINEAR_SCALE );
+ }
+ else
+ {
+ AssertIf( width > image->GetWidth() || height > image->GetHeight() );
+ image->BlitImage( clipped, ImageReference::BLIT_COPY );
+ PadImageBorder( *image, width, height );
+ }
+ return true;
+ }
+ else
+ {
+ ImageReference source( width, height, width * GetBytesFromTextureFormat(rep.format),
+ rep.format, rep.data + imageIndex * rep.imageSize + mipmapoffset);
+
+ if( scaleToSize )
+ {
+ image->BlitImage( source, ImageReference::BLIT_BILINEAR_SCALE );
+ }
+ else
+ {
+ AssertIf( width > image->GetWidth() || height > image->GetHeight() );
+ image->BlitImage( source, ImageReference::BLIT_COPY );
+ PadImageBorder( *image, width, height );
+ }
+ return true;
+ }
+
+ return false;
+}
+bool Texture2D::ExtractImage (ImageReference* image, int imageIndex) const
+{
+ return ExtractImageInternal( image, true, imageIndex );
+}
+
+bool Texture2D::HasMipMap() const {
+ return m_MipMap;
+}
+
+int Texture2D::CountMipmaps() const
+{
+ if (m_MipMap)
+ return CalculateMipMapCount3D( m_glWidth, m_glHeight, 1 );
+ else
+ return 1;
+}
+int Texture2D::CountDataMipmaps() const
+{
+ if (m_MipMap)
+ return CalculateMipMapCount3D( m_TexData.width, m_TexData.height, 1 );
+ else
+ return 1;
+}
+
+UInt8* Texture2D::AllocateTextureData (int imageSize, TextureFormat format, bool initMemory)
+{
+ // Allocate one more pixel because software bi-linear filtering might require it.
+ int allocSize = imageSize + GetBytesForOnePixel(format);
+ void* buffer = UNITY_MALLOC_ALIGNED (GetTextureDataMemoryLabel(), allocSize, 32);
+
+ // In the past the memory manager cleared textures created from script to 0xcd.
+ // Let's keep doing that even though a color like white makes more sense (case 564961).
+ if (initMemory && buffer)
+ memset(buffer, 0xcd, allocSize);
+
+ return static_cast<UInt8*>(buffer);
+}
+
+void Texture2D::DeallocateTextureData (UInt8* tex)
+{
+ UNITY_FREE (GetTextureDataMemoryLabel(), tex);
+}
+
+void Texture2D::InitTextureInternal (int width, int height, TextureFormat format, int imageSize, UInt8* buffer, int options, int imageCount)
+{
+ // Cleanup old memory
+ if ((options & kThreadedInitialize) == 0)
+ {
+ DestroyTexture ();
+ SetDirty ();
+ }
+ else
+ {
+ AssertIf(m_TexData.data != NULL);
+ }
+
+ m_TextureDimension = kTexDim2D;
+
+ m_TexData.width = width;
+ m_TexData.height = height;
+ m_TexData.format = format;
+ m_TexData.imageSize = imageSize;
+ m_TexData.data = buffer;
+ m_MipMap = options & kMipmapMask;
+ m_InitFlags = options;
+ m_ImageCount = imageCount;
+ UpdatePOTStatus();
+
+ m_glWidth = GetNextAllowedTextureSize(m_TexData.width, m_MipMap, format);
+ m_glHeight = GetNextAllowedTextureSize(m_TexData.height, m_MipMap, format);
+ SetTexelSize( 1.0f/m_glWidth, 1.0f/m_glHeight );
+}
+
+bool Texture2D::InitTexture (int width, int height, TextureFormat format, int options, int imageCount, intptr_t nativeTex)
+{
+ SET_ALLOC_OWNER(this);
+ if (width < 0 || width > 10000 || height < 0 || height > 10000)
+ {
+ ErrorStringObject ("Texture has out of range width / height", this);
+ return false;
+ }
+
+ if (!IsValidTextureFormat(format))
+ {
+ ErrorStringObject ("TextureFormat is invalid!", this);
+ return false;
+ }
+
+ int imageSize;
+ if( options & kMipmapMask)
+ imageSize = CalculateImageMipMapSize( width, height, format );
+ else
+ imageSize = CalculateImageSize( width, height, format );
+
+ unsigned int tlen = imageSize * imageCount;
+ // probably an multiplication overflow
+ if (imageSize != 0 && imageCount != tlen / imageSize)
+ return false;
+ // probably an addition overflow
+ if (tlen + GetBytesForOnePixel(format) < tlen)
+ return false;
+
+ bool allocData = ENABLE_TEXTUREID_MAP == 0 || nativeTex == 0;
+
+#if ENABLE_TEXTUREID_MAP
+ if(nativeTex)
+ TextureIdMap::UpdateTexture(m_TexID, GetGfxDevice().CreateExternalTextureFromNative(nativeTex));
+#endif
+
+ UInt8* buffer = allocData ? AllocateTextureData(imageSize * imageCount, format, true) : 0;
+ InitTextureInternal (width, height, format, imageSize, buffer, options, imageCount);
+
+ return true;
+}
+
+void Texture2D::RebuildMipMap ()
+{
+ TextureRepresentation& rep = m_TexData;
+
+ if( rep.data == NULL || !m_MipMap )
+ return;
+ if( IsAnyCompressedTextureFormat(rep.format) )
+ {
+ ErrorString ("Rebuilding mipmaps of compressed textures is not supported");
+ return;
+ }
+
+ for( int i=0;i<m_ImageCount;i++ )
+ CreateMipMap (rep.data + rep.imageSize * i, rep.width, rep.height, 1, rep.format);
+}
+
+
+#if UNITY_EDITOR
+void Texture2D::SetImage (const ImageReference& image, int flags)
+{
+ SET_ALLOC_OWNER(this);
+ InitTexture( image.GetWidth (), image.GetHeight (), image.GetFormat (), flags );
+
+ int bytesPerPixel = GetBytesFromTextureFormat (m_TexData.format);
+
+ ImageReference dst( m_TexData.width, m_TexData.height, bytesPerPixel * m_TexData.width, m_TexData.format, m_TexData.data );
+ if (image.GetImageData () != NULL)
+ dst.BlitImage (image);
+
+ RebuildMipMap ();
+
+ UploadTexture (true);
+ SetDirty ();
+}
+#endif
+
+
+
+bool Texture2D::GetImageReferenceInternal (ImageReference* image, int frame, int miplevel) const
+{
+ TextureRepresentation const& rep = m_TexData;
+
+ if( rep.data == NULL || IsAnyCompressedTextureFormat(rep.format) )
+ return false;
+
+ AssertIf (frame >= m_ImageCount);
+ AssertIf (miplevel >= CountDataMipmaps ());
+
+ UInt8* base = rep.data + frame * rep.imageSize;
+ base += CalculateMipMapOffset( rep.width, rep.height, rep.format, miplevel );
+ int width = std::max (rep.width >> miplevel, 1);
+ int height = std::max (rep.height >> miplevel, 1);
+
+ *image = ImageReference( width, height, GetRowBytesFromWidthAndFormat(width, rep.format), rep.format, base );
+ return true;
+}
+
+bool Texture2D::GetWriteImageReference (ImageReference* image, int frame, int miplevel)
+{
+ return GetImageReferenceInternal (image, frame, miplevel);
+}
+
+
+void Texture2D::ApplySettings()
+{
+ TextureDimension texdim = GetDimension();
+ m_TextureSettings.Apply( GetTextureID(), texdim, HasMipMap(), GetActiveTextureColorSpace());
+ if( m_UnscaledTextureUploaded )
+ m_TextureSettings.Apply( GetUnscaledTextureID(), texdim, HasMipMap(), GetActiveTextureColorSpace() );
+
+ NotifyMipBiasChanged();
+}
+
+void Texture2D::UpdatePOTStatus()
+{
+ m_PowerOfTwo = IsPowerOfTwo(m_TexData.width) && IsPowerOfTwo(m_TexData.height);
+
+ // force clamp if we will keep it npot
+ if(!m_PowerOfTwo && !m_MipMap && gGraphicsCaps.npot == kNPOTRestricted)
+ m_TextureSettings.m_WrapMode = kTexWrapClamp;
+}
+
+
+void Texture2D::UploadTexture (bool dontUseSubImage)
+{
+ if (m_TexData.data == NULL)
+ {
+ ErrorString("No Texture memory available to upload");
+ return;
+ }
+ if (m_TexData.width == 0 || m_TexData.height == 0)
+ {
+ return;
+ }
+
+ TextureRepresentation scaled = m_TexData;
+ TextureRepresentation padded = m_TexData;
+
+ InitTextureRepresentations(&scaled, &padded);
+
+ int mipCount = CountMipmaps();
+
+ int masterTextureLimit = Texture::GetMasterTextureLimit();
+ #if UNITY_EDITOR
+ if (m_IgnoreMasterTextureLimit)
+ masterTextureLimit = 0;
+ #endif
+
+ // Master texture limit must be clamped to mipCount-1 or otherwise GfxDevice won't upload anything
+ masterTextureLimit = min(mipCount-1, masterTextureLimit);
+
+ TextureUsageMode usageMode = GetUsageMode();
+
+ // upload regular texture
+ {
+ TextureRepresentation& rep = scaled;
+ UInt8* srcData = rep.data;
+ UInt32 uploadFlags = (dontUseSubImage || !m_TextureUploaded) ? GfxDevice::kUploadTextureDontUseSubImage : GfxDevice::kUploadTextureDefault;
+ if (m_InitFlags & kOSDrawingCompatible)
+ uploadFlags |= GfxDevice::kUploadTextureOSDrawingCompatible;
+ GetGfxDevice().UploadTexture2D( GetTextureID(), GetDimension(), srcData, rep.imageSize, rep.width, rep.height, rep.format, mipCount, uploadFlags, masterTextureLimit, usageMode, GetActiveTextureColorSpace() );
+ Texture::s_TextureIDMap.insert (std::make_pair(GetTextureID(),this));
+ m_TextureSettings.Apply( GetTextureID(), GetDimension(), m_MipMap, GetActiveTextureColorSpace());
+ m_TextureUploaded = true;
+ }
+
+ // upload unscaled one if NPOT and not supported
+ if( m_TexData.width != m_glWidth || m_TexData.height != m_glHeight )
+ {
+ TextureRepresentation& rep = padded;
+ UInt8* srcData = rep.data;
+
+ UInt32 uploadFlags = (dontUseSubImage || !m_UnscaledTextureUploaded) ? GfxDevice::kUploadTextureDontUseSubImage : GfxDevice::kUploadTextureDefault;
+ if (m_InitFlags & kOSDrawingCompatible)
+ uploadFlags |= GfxDevice::kUploadTextureOSDrawingCompatible;
+
+ m_UnscaledTextureUploaded = true; // must be before Upload in order for GetUnscaledGLTextureName() to return the right value
+ TextureID tid = GetUnscaledTextureID();
+ GetGfxDevice().UploadTexture2D( tid, GetDimension(), srcData, rep.imageSize, rep.width, rep.height, rep.format, mipCount, uploadFlags, masterTextureLimit, usageMode, GetActiveTextureColorSpace() );
+ Texture::s_TextureIDMap.insert (std::make_pair(tid,this));
+ m_TextureSettings.Apply( tid, GetDimension(), m_MipMap, GetActiveTextureColorSpace() );
+ }
+
+#if UNITY_XENON && !MASTER_BUILD
+ GetGfxDevice().SetTextureName( GetTextureID(), GetName() );
+#endif
+
+#if UNITY_EDITOR
+ DestroyTextureRepresentations(&scaled, &padded, false);
+#elif UNITY_WII
+ // On Wii texture data is being directly referenced from m_TexData.data, so don't delete it after uploading
+ DestroyTextureRepresentations(&scaled, &padded, false);
+#else
+ DestroyTextureRepresentations(&scaled, &padded, !m_IsReadable);
+ if(!m_IsReadable)
+ m_TexData.imageSize = 0;
+#endif
+}
+
+void Texture2D::DestroyTextureRepresentation( TextureRepresentation* rep )
+{
+ if( rep )
+ {
+ if( rep->data == m_TexData.data )
+ rep->data = NULL;
+
+ DeallocateTextureData (rep->data);
+ rep->data = NULL;
+ }
+}
+
+void Texture2D::DestroyTextureRepresentations( TextureRepresentation* scaled, TextureRepresentation* padded, bool freeSourceImage )
+{
+ DestroyTextureRepresentation(padded);
+ DestroyTextureRepresentation(scaled);
+
+ if( freeSourceImage )
+ {
+ DeallocateTextureData (m_TexData.data);
+ m_TexData.data = NULL;
+ }
+}
+
+void Texture2D::DeleteGfxTexture ()
+{
+ if (m_TextureUploaded)
+ {
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ GetGfxDevice().DeleteTexture( GetTextureID() );
+ m_TextureUploaded = false;
+ }
+ if (m_UnscaledTextureUploaded)
+ {
+ ASSERT_RUNNING_ON_MAIN_THREAD
+ GetGfxDevice().DeleteTexture( GetUnscaledTextureID() );
+ m_UnscaledTextureUploaded = false;
+ }
+}
+
+
+void Texture2D::DestroyTexture ()
+{
+ DestroyTextureRepresentations (0,0,true);
+
+ DeleteGfxTexture ();
+}
+
+
+void Texture2D::UnloadFromGfxDevice(bool forceUnloadAll)
+{
+ if (m_IsUnreloadable && !forceUnloadAll)
+ return;
+ DeleteGfxTexture ();
+}
+
+void Texture2D::UploadToGfxDevice()
+{
+ if (m_IsUnreloadable)
+ return;
+
+ GfxDevice& device = GetGfxDevice();
+
+ // We need to load the texture data from disk. Awake FromLoad will be called during ReloadFromDisk.
+ bool needReloadFromDisk = (m_TexData.data == NULL && !m_IsReadable);
+ if (needReloadFromDisk)
+ {
+ TextureSettings settings = m_TextureSettings;
+
+ GetPersistentManager().ReloadFromDisk(this);
+
+ m_TextureSettings = settings;
+ ApplySettings();
+ }
+ // Just upload the texture data we already have in memory
+ else
+ {
+ UploadTexture (true);
+ }
+}
+
+
+#if UNITY_EDITOR
+struct TemporaryTextureSerializationRevert
+{
+ Texture2D* texture;
+ Texture2D::TextureRepresentation texData;
+ int imageCount;
+ bool isReadable;
+
+ TemporaryTextureSerializationRevert (Texture2D& tex2D, bool doRevert)
+ {
+ texture = NULL;
+
+ if (doRevert)
+ {
+ texture = &tex2D;
+
+ texData = texture->m_TexData;
+ imageCount = texture->m_ImageCount;
+ isReadable = texture->m_IsReadable;
+ }
+ }
+ ~TemporaryTextureSerializationRevert ()
+ {
+ if (texture != NULL)
+ {
+ texture->m_TexData = texData;
+ texture->m_ImageCount = imageCount;
+ texture->m_IsReadable = isReadable;
+ }
+ }
+};
+#endif
+
+template<class TransferFunction>
+void Texture2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+#if UNITY_EDITOR
+
+ // Store serialization state temporarliy and revert it when exiting function
+ TemporaryTextureSerializationRevert revert(*this, !transfer.IsReading ());
+
+ // When writing dynamic font textures we don't want to write any texture data to disk
+ if (m_EditorDontWriteTextureData && !transfer.IsReading ())
+ {
+ m_ImageCount = m_TexData.imageSize = m_TexData.height = m_TexData.width = 0;
+ m_TexData.data = NULL;
+ }
+
+ // readable flag gets adjusted by forced readable flag
+ if (transfer.IsBuildingTargetPlatform(kBuildAnyPlayerData))
+ m_IsReadable |= transfer.GetBuildUsage().forceTextureReadable;
+
+ TRANSFER_EDITOR_ONLY_HIDDEN(m_AlphaIsTransparency);
+ transfer.Align();
+#endif
+
+ // In case we're converting the texture data to another format, we also properly write out the format ID.
+ transfer.Transfer( m_TexData.width, "m_Width", kNotEditableMask );
+ transfer.Transfer( m_TexData.height, "m_Height", kNotEditableMask );
+ transfer.Transfer( m_TexData.imageSize, "m_CompleteImageSize", kNotEditableMask );
+ transfer.Transfer( m_TexData.format, "m_TextureFormat", kHideInEditorMask );
+ transfer.Transfer( m_MipMap, "m_MipMap", kNotEditableMask );
+
+ transfer.Transfer( m_IsReadable, "m_IsReadable");
+ transfer.Transfer( m_ReadAllowed, "m_ReadAllowed", kNotEditableMask );
+ transfer.Align();
+ transfer.Transfer( m_ImageCount, "m_ImageCount", kNotEditableMask );
+ transfer.Transfer( m_TextureDimension, "m_TextureDimension", kHideInEditorMask );
+ transfer.Transfer( m_TextureSettings, "m_TextureSettings");
+ transfer.Transfer( m_UsageMode, "m_LightmapFormat");
+ transfer.Transfer( m_ColorSpace, "m_ColorSpace");
+
+ unsigned imageSizeXImageCount = m_ImageCount * m_TexData.imageSize;
+
+ if (!transfer.IsWritingGameReleaseData ())
+ transfer.TransferTypeless (&imageSizeXImageCount, "image data", kHideInEditorMask);
+
+ if (transfer.IsReading ())
+ {
+ DestroyTexture ();
+ Assert(GetMemoryLabel().label != kMemTextureCacheId);
+ m_TexData.data = AllocateTextureData(imageSizeXImageCount, m_TexData.format);
+ m_glWidth = GetNextAllowedTextureSize(m_TexData.width, m_MipMap, TextureFormat(m_TexData.format));
+ m_glHeight = GetNextAllowedTextureSize(m_TexData.height, m_MipMap, TextureFormat(m_TexData.format));
+ SetTexelSize( 1.0f/m_glWidth, 1.0f/m_glHeight );
+ }
+
+ // write tex image
+ if (transfer.IsWriting())
+ {
+ const bool gpuBE = UNITY_EDITOR ? transfer.IsBuildingTargetPlatform(kBuildXBOX360) : false;
+
+ UInt8* convertedData = NULL;
+ if( transfer.ConvertEndianess() )
+ {
+ convertedData = AllocateTextureData(imageSizeXImageCount, m_TexData.format);
+ ConvertTextureEndianessWrite(m_TexData.format, m_TexData.data, convertedData, imageSizeXImageCount, gpuBE);
+ }
+
+ if (transfer.IsWritingGameReleaseData ())
+ transfer.TransferTypeless (&imageSizeXImageCount, "image data", kHideInEditorMask);
+ transfer.TransferTypelessData (imageSizeXImageCount, convertedData ? convertedData : m_TexData.data );
+
+ DeallocateTextureData(convertedData);
+ }
+ else
+ {
+ if( transfer.ConvertEndianess() )
+ {
+ transfer.TransferTypelessData (imageSizeXImageCount, m_TexData.data);
+ ConvertTextureEndianessRead (m_TexData.format, m_TexData.data, imageSizeXImageCount);
+ }
+ else
+ {
+ transfer.TransferTypelessData (imageSizeXImageCount, m_TexData.data);
+ }
+ }
+
+ AssertIf ( m_TexData.imageSize != 0 && m_TexData.data == NULL );
+
+ if( transfer.IsReading() )
+ UpdatePOTStatus();
+}
+
+void ConvertTextureEndianessWrite (int format, UInt8* src, UInt8* dst, int size, bool bBigEndianGPU)
+{
+ memcpy (dst, src, size);
+ if (format == kTexFormatARGBFloat)
+ SwapEndianArray (dst, 4, size / 4);
+ else if (format == kTexFormatRGB565 || format == kTexFormatARGB4444 || format == kTexFormatRGBA4444)
+ SwapEndianArray (dst, 2, size / 2);
+ else if (bBigEndianGPU && (format == kTexFormatDXT1 || format == kTexFormatDXT3 || format == kTexFormatDXT5))
+ SwapEndianArray (dst, 2, size / 2);
+
+}
+
+void ConvertTextureEndianessRead (int format, UInt8* dst, int size)
+{
+ if (format == kTexFormatARGBFloat)
+ SwapEndianArray (dst, 4, size / 4);
+ else if (format == kTexFormatRGB565 || format == kTexFormatARGB4444 || format == kTexFormatRGBA4444)
+ SwapEndianArray (dst, 2, size / 2);
+}
+
+
+void Texture2D::UpdateImageData ()
+{
+ RebuildMipMap ();
+ UploadTexture (false);
+ SetDirty ();
+}
+
+void Texture2D::UpdateImageDataDontTouchMipmap ()
+{
+ UploadTexture (false);
+ SetDirty ();
+}
+
+
+TextureID Texture2D::GetUnscaledTextureID() const
+{
+ return m_UnscaledTextureUploaded ? m_UnscaledTexID : m_TexID;
+}
+int Texture2D::GetDataWidth() const
+{
+ return m_TexData.width;
+}
+int Texture2D::GetDataHeight() const
+{
+ return m_TexData.height;
+}
+
+
+void Texture2D::InitTextureRepresentation( TextureRepresentation* rep, int format, const char* tag )
+{
+ Assert(rep);
+
+ rep->width = GetNextAllowedTextureSize(m_TexData.width, m_MipMap, TextureFormat(format));
+ rep->height = GetNextAllowedTextureSize(m_TexData.height, m_MipMap, TextureFormat(format));
+ rep->format = format;
+
+ if( m_MipMap )
+ rep->imageSize = CalculateImageMipMapSize( rep->width, rep->height, rep->format );
+ else
+ rep->imageSize = CalculateImageSize( rep->width, rep->height, rep->format );
+
+ rep->data = AllocateTextureData(rep->imageSize * m_ImageCount, rep->format);
+}
+
+void Texture2D::ExtractMipLevel( TextureRepresentation* dst, int frame, int mipLevel, bool checkCompression, bool scaleToSize )
+{
+ if (dst->width == 0 || dst->height == 0)
+ return;
+
+ UInt8* data = dst->data + frame * dst->imageSize + CalculateMipMapOffset(dst->width, dst->height, dst->format, mipLevel );
+
+
+ int width = std::max( dst->width >> mipLevel, 1 );
+ int height = std::max( dst->height >> mipLevel, 1 );
+
+ if( checkCompression && IsAnyCompressedTextureFormat(dst->format) )
+ {
+ ExtractCompressedImageInternal( data, width, height, frame );
+ }
+ else
+ {
+ ImageReference ref( width, height, width*GetBytesFromTextureFormat(dst->format), dst->format, data );
+ ExtractImageInternal( &ref, scaleToSize, frame );
+ }
+}
+
+
+void Texture2D::InitTextureRepresentations(Texture2D::TextureRepresentation* scaled, Texture2D::TextureRepresentation* padded)
+{
+ Assert(scaled);
+ Assert(padded);
+
+ if( m_TextureDimension == kTexDimDeprecated1D )
+ m_TextureDimension = kTexDim2D;
+
+ int multipleMask = GetTextureSizeAllowedMultiple(TextureFormat(m_TexData.format)) - 1;
+ bool isAllowedMultiple = (m_TexData.width & multipleMask) == 0 && (m_TexData.height & multipleMask) == 0;
+ if (isAllowedMultiple && (m_PowerOfTwo || IsNPOTTextureAllowed(m_MipMap)))
+ {
+ *scaled = *padded = m_TexData;
+ SetTexelSize( 1.0f / m_TexData.width, 1.0f / m_TexData.height );
+ return;
+ }
+
+ DebugAssertIf( m_PowerOfTwo );
+ DebugAssertIf( !m_TexData.data );
+
+ InitTextureRepresentation(scaled, IsAnyCompressedTextureFormat(m_TexData.format) ? kTexFormatRGBA32 : m_TexData.format, "tex.scaled");
+ InitTextureRepresentation(padded, m_TexData.format, "tex.padded");
+
+ int mipcount = CountMipmaps();
+ for( int frame = 0; frame < m_ImageCount; ++frame )
+ {
+ for( int mip = 0; mip < mipcount; ++mip )
+ {
+ ExtractMipLevel(scaled, frame, mip, false, true);
+ ExtractMipLevel(padded, frame, mip, true, false);
+ }
+ }
+}
+
+int Texture2D::GetRuntimeMemorySize() const
+{
+#if ENABLE_MEM_PROFILER
+ return Super::GetRuntimeMemorySize() +
+ GetMemoryProfiler()->GetRelatedIDMemorySize(m_TexID.m_ID) +
+ (m_UnscaledTextureUploaded?GetMemoryProfiler()->GetRelatedIDMemorySize(GetUnscaledTextureID().m_ID):0);
+#endif
+ return sizeof(Texture2D);
+}
+
+
+IMPLEMENT_CLASS (Texture2D)
+IMPLEMENT_OBJECT_SERIALIZE (Texture2D)
+INSTANTIATE_TEMPLATE_TRANSFER (Texture2D)
+
+bool Texture2D::CheckHasPixelData () const
+{
+ if (m_TexData.data != NULL)
+ return true;
+
+ if (!m_IsReadable)
+ {
+ ErrorString(Format("Texture '%s' is not readable, the texture memory can not be accessed from scripts. You can make the texture readable in the Texture Import Settings.", GetName()));
+ }
+ else
+ {
+ ErrorString(Format("Texture '%s' has no data", GetName()));
+ }
+ return false;
+}
+
+
+void Texture2D::SetPixel (int frame, int x, int y, const ColorRGBAf& c)
+{
+ if (!CheckHasPixelData())
+ return;
+
+ if (frame > m_ImageCount) {
+ ErrorString (Format ("SetPixel called on an undefined image (valid values are 0 - %d", m_ImageCount - 1));
+ return;
+ }
+
+ ImageReference image;
+ if (GetWriteImageReference(&image, frame, 0))
+ {
+ SetImagePixel (image, x, y, static_cast<TextureWrapMode>(GetSettings().m_WrapMode), c);
+ }
+ else
+ {
+ if( IsAnyCompressedTextureFormat(m_TexData.format) )
+ {
+ ErrorString(kUnsupportedSetPixelOpFormatMessage);
+ }
+ else
+ {
+ ErrorString("Unable to retrieve image reference");
+ }
+ }
+}
+
+void Texture2D::SetPixels( int x, int y, int width, int height, int pixelCount, const ColorRGBAf* pixels, int miplevel, int frame )
+{
+ if (width == 0 || height == 0)
+ return; // nothing to do
+
+ if (!CheckHasPixelData())
+ return;
+
+ int mipcount = CountMipmaps();
+ if (miplevel < 0 || miplevel >= mipcount)
+ {
+ ErrorString ("Invalid mip level");
+ return;
+ }
+
+ UInt8* data = m_TexData.data + frame * m_TexData.imageSize;
+ data += CalculateMipMapOffset (m_TexData.width, m_TexData.height, m_TexData.format, miplevel);
+ int dataWidth = std::max (m_TexData.width >> miplevel, 1);
+ int dataHeight = std::max (m_TexData.height >> miplevel, 1);
+
+ SetImagePixelBlock (data, dataWidth, dataHeight, m_TexData.format, x, y, width, height, pixelCount, pixels);
+}
+
+
+
+ColorRGBAf Texture2D::GetPixel (int frame,int x, int y) const
+{
+ if (!CheckHasPixelData())
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+
+ if (frame > m_ImageCount) {
+ ErrorString (Format ("GetPixel called on an undefined image (valid values are 0 - %d", m_ImageCount - 1));
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ }
+
+ return GetImagePixel (m_TexData.data + frame*m_TexData.imageSize, m_TexData.width, m_TexData.height, m_TexData.format, static_cast<TextureWrapMode>(GetSettings().m_WrapMode), x, y);
+}
+
+ColorRGBAf Texture2D::GetPixelBilinear (int frame, float u, float v) const
+{
+ if (!CheckHasPixelData())
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ if (frame > m_ImageCount) {
+ ErrorString (Format ("GetPixelBilinear called on an undefined image (valid values are 0 - %d", m_ImageCount - 1));
+ return ColorRGBAf(1.0F,1.0F,1.0F,1.0F);
+ }
+ return GetImagePixelBilinear (m_TexData.data + frame*m_TexData.imageSize, m_TexData.width, m_TexData.height, m_TexData.format, static_cast<TextureWrapMode>(GetSettings().m_WrapMode), u, v);
+}
+
+bool Texture2D::GetPixels( int x, int y, int width, int height, int miplevel, ColorRGBAf* colors, int frame ) const
+{
+ if (width == 0 || height == 0)
+ return true; // nothing to do
+
+ if (!CheckHasPixelData())
+ return false;
+
+ int mipcount = CountMipmaps();
+ if (miplevel < 0 || miplevel >= mipcount)
+ {
+ ErrorString ("Invalid mip level");
+ return false;
+ }
+
+ UInt8* data = m_TexData.data + frame * m_TexData.imageSize;
+ data += CalculateMipMapOffset (m_TexData.width, m_TexData.height, m_TexData.format, miplevel);
+ int dataWidth = std::max (m_TexData.width >> miplevel, 1);
+ int dataHeight = std::max (m_TexData.height >> miplevel, 1);
+
+ return GetImagePixelBlock (data, dataWidth, dataHeight, m_TexData.format, x, y, width, height, colors);
+}
+
+bool Texture2D::GetPixels32( int miplevel, ColorRGBA32* colors ) const
+{
+ AssertIf( miplevel < 0 || miplevel >= CountDataMipmaps() );
+
+ ImageReference texImage;
+ if( !GetImageReferenceInternal(&texImage, 0, miplevel) )
+ {
+ if (m_TexData.data != NULL && IsAnyCompressedTextureFormat(m_TexData.format))
+ {
+ UInt8* data = m_TexData.data + CalculateMipMapOffset( m_TexData.width, m_TexData.height, m_TexData.format, miplevel );
+ const int minSize = GetMinimumTextureMipSizeForFormat(m_TexData.format);
+ // the decompress size is at least minSize x minSize
+ int width = std::max (m_TexData.width >> miplevel, minSize);
+ int height = std::max (m_TexData.height >> miplevel, minSize);
+ // also, it should be a multiple of minSize
+ if (width % minSize == 0 && height % minSize == 0)
+ {
+ DecompressNativeTextureFormatWithMipLevel( m_TexData.format, width, height,miplevel, reinterpret_cast<const UInt32*>(data), width, height, reinterpret_cast<UInt32*>(colors) );
+ return true;
+ }
+ else
+ {
+ // make it an upper multiple of minSize
+ int decompWidth = (width + minSize - 1) / minSize * minSize;
+ int decompHeight = (height + minSize - 1) / minSize * minSize;
+ Image decompressed( decompWidth, decompHeight, kTexFormatRGBA32 );
+ DecompressNativeTextureFormatWithMipLevel( m_TexData.format, width, height,miplevel, reinterpret_cast<const UInt32*>(data), decompWidth, decompHeight, (UInt32*)decompressed.GetImageData() );
+ // clip it back to the original size
+ ImageReference clipped = decompressed.ClipImage( 0, 0, width, height );
+ // blit it over to the colors array
+ ImageReference resultImage( width, height, GetRowBytesFromWidthAndFormat(width,kTexFormatRGBA32), kTexFormatRGBA32, colors );
+ resultImage.BlitImage( clipped, ImageReference::BLIT_COPY );
+ return true;
+ }
+ }
+ AssertString( "Invalid texture" );
+ return false;
+ }
+
+ int texWidth = texImage.GetWidth();
+ int texHeight = texImage.GetHeight();
+
+ ImageReference resultImage( texWidth, texHeight, GetRowBytesFromWidthAndFormat(texWidth,kTexFormatRGBA32), kTexFormatRGBA32, colors );
+ resultImage.BlitImage( texImage, ImageReference::BLIT_COPY );
+
+ return true;
+}
+
+
+void Texture2D::SetPixels32( int miplevel, const ColorRGBA32* pixels, const int pixelCount )
+{
+ AssertIf( miplevel < 0 || miplevel >= CountMipmaps() );
+
+ ImageReference texImage;
+ if( !GetWriteImageReference(&texImage, 0, miplevel) )
+ {
+ AssertString( "Invalid texture format" );
+ return;
+ }
+
+ int texWidth = texImage.GetWidth();
+ int texHeight = texImage.GetHeight();
+
+ if(texWidth * texHeight != pixelCount)
+ {
+ AssertString( "SetPixels32 called with invalid number if pixels in the array" );
+ return;
+ }
+
+ ImageReference inputImage( texWidth, texHeight, GetRowBytesFromWidthAndFormat(texWidth,kTexFormatRGBA32), kTexFormatRGBA32, (void*)pixels );
+ texImage.BlitImage( inputImage, ImageReference::BLIT_COPY );
+}
+
+static const char* kUnsupportedColorPixelFormatMessage = "Unsupported image format - the texture needs to be ARGB32 or RGB24";
+
+static bool IsAllowedToReadPixels ()
+{
+#if UNITY_EDITOR
+ // from Player.h
+ bool IsInsidePlayerLoop ();
+ // Editor needs to read frame buffer outside player loop
+ if( !IsInsidePlayerLoop() )
+ return true;
+#elif WEBPLUG
+ // Allow old web content for compatibility
+ if( !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1) )
+ return true;
+#endif
+ return GetGfxDevice().IsInsideFrame() || RenderTexture::GetActive();
+}
+
+// TODO: Check dimensions, make everything work.
+void Texture2D::ReadPixels (int frame, int left, int bottom, int width, int height, int destX, int destY, bool flipped, bool computeMipMap)
+{
+ //Prevent out of bounds reads (case 562067)
+ if (destX < 0 || destY < 0 || destX >= GetDataWidth() || destY >= GetDataHeight())
+ {
+ ErrorString("Trying to read pixels out of bounds");
+ return;
+ }
+ if (width < 0 || height < 0)
+ {
+ ErrorString("Negative read pixels rectangle width|height");
+ return;
+ }
+
+ if( !IsAllowedToReadPixels() )
+ {
+ ErrorString ("ReadPixels was called to read pixels from system frame buffer, while not inside drawing frame.");
+ // Our tests rely on calling ReadImage() during Update() so we allow it anyway...
+ }
+
+ if( frame >= m_ImageCount )
+ {
+ ErrorString (Format ("ReadPixels called on undefined image %d (valid values are 0 - %d", frame, m_ImageCount - 1));
+ return;
+ }
+
+ GfxDeviceRenderer renderer = GetGfxDevice().GetRenderer();
+ bool isGLES = (renderer == kGfxRendererOpenGLES20Mobile || renderer == kGfxRendererOpenGLES30);
+ int texFormat = GetTextureFormat();
+ bool validFormat = texFormat == kTexFormatARGB32 ||
+ texFormat == kTexFormatRGB24 ||
+ texFormat == (kTexFormatRGBA32 && isGLES) ||
+ texFormat == (kTexFormatRGB565 && isGLES);
+ if( !validFormat )
+ {
+ ErrorString(kUnsupportedColorPixelFormatMessage);
+ return;
+ }
+
+ ImageReference image;
+ if( !GetWriteImageReference(&image, frame, 0) )
+ {
+ ErrorString("Unable to retrieve image reference");
+ return;
+ }
+
+ if (RenderTexture::GetActive() == NULL)
+ {
+ Rectf rc = GetRenderManager().GetWindowRect();
+ left += rc.x;
+ bottom += rc.y;
+ }
+
+ if (left < 0) {
+ width += left;
+ left = 0;
+ }
+ if (bottom < 0) {
+ height += bottom;
+ bottom = 0;
+ }
+ if (destX + width > GetDataWidth())
+ width = GetDataWidth() - destX;
+ if (destY + height > GetDataHeight())
+ height = GetDataHeight() - destY;
+
+ GetGfxDevice().ReadbackImage( image, left, bottom, width, height, destX, destY );
+
+ if (flipped)
+ {
+ ImageReference subImage = image.ClipImage (destX, destY, width, height);
+ subImage.FlipImageY();
+ }
+
+ if( computeMipMap && m_MipMap )
+ RebuildMipMap();
+}
+
+
+#if ENABLE_PNG_JPG
+bool Texture2D::EncodeToPNG( dynamic_array<UInt8>& outBuffer )
+{
+ if( IsAnyCompressedTextureFormat(GetTextureFormat()) )
+ {
+ ErrorString(kUnsupportedSetPixelOpFormatMessage);
+ return false;
+ }
+ ImageReference image;
+ if( !GetWriteImageReference( &image, 0, 0 ) )
+ {
+ ErrorString( "Unable to retrieve image reference" );
+ return false;
+ }
+ if( !ConvertImageToPNGBuffer( image, outBuffer ) )
+ {
+ ErrorString( "Failed to encode to PNG" );
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+
+
+bool Texture2D::ResizeWithFormat (int width, int height, TextureFormat format, int flags)
+{
+ if (!m_IsReadable)
+ {
+ ErrorString ("Texture is not readable.");
+ return false;
+ }
+
+ if( IsAnyCompressedTextureFormat(format) )
+ {
+ ErrorStringObject ("Can't resize to a compressed texture format", this);
+ return false;
+ }
+
+ return InitTexture(width, height, format, flags);
+}
+
+
+void Texture2D::Compress (bool dither)
+{
+ if (!m_IsReadable)
+ {
+ ErrorString(Format("Texture '%s' is not readable, Compress will not work. You can make the texture readable in the Texture Import Settings.", GetName()));
+ return;
+ }
+
+ SET_ALLOC_OWNER(this);
+ // If hardware does not support DXT, then nothing to do.
+ if( !gGraphicsCaps.hasS3TCCompression )
+ return;
+
+ #if !GFX_SUPPORTS_DXT_COMPRESSION
+ Assert(!"GraphicsCaps.hasS3TCCompression is invalid");
+ return;
+ #else
+
+ TextureFormat format = GetTextureFormat();
+ // If already compressed, then nothing to do.
+ if( IsAnyCompressedTextureFormat(format) )
+ return;
+
+ // Copy out old data into RGBA32 format.
+ bool mipMaps = HasMipMap();
+ int width = GetDataWidth();
+ int height = GetDataHeight();
+ int rgbaByteSize = mipMaps ?
+ CalculateImageMipMapSize( width, height, kTexFormatRGBA32 ) :
+ CalculateImageSize( width, height, kTexFormatRGBA32 );
+ UInt8* rgbaData = new UInt8[rgbaByteSize];
+ int mipCount = CountDataMipmaps();
+ for( int mip = 0; mip < mipCount; ++mip )
+ {
+ UInt8* rgbaMipData = rgbaData + CalculateMipMapOffset(width, height, kTexFormatRGBA32, mip);
+ int mipWidth = std::max( width >> mip, 1 );
+ int mipHeight = std::max( height >> mip, 1 );
+ ImageReference mipDst( mipWidth, mipHeight, mipWidth * 4, kTexFormatRGBA32, rgbaMipData );
+ ExtractImageInternal( &mipDst, false, 0 );
+ }
+
+ // Reformat texture into DXT format
+ bool hasAlpha = HasAlphaTextureFormat(format);
+ TextureFormat compressedFormat = hasAlpha ? kTexFormatDXT5 : kTexFormatDXT1;
+ InitTexture( width, height, compressedFormat, mipMaps ? kMipmapMask : kNoMipmap );
+
+ // DXT compress RGBA data
+ for( int mip = 0; mip < mipCount; ++mip )
+ {
+ const UInt8* srcMipData = rgbaData + CalculateMipMapOffset(width, height, kTexFormatRGBA32, mip);
+ UInt8* dstMipData = GetRawImageData() + CalculateMipMapOffset(width, height, compressedFormat, mip);
+ int mipWidth = std::max( width >> mip, 1 );
+ int mipHeight = std::max( height >> mip, 1 );
+ FastCompressImage( mipWidth, mipHeight, srcMipData, dstMipData, hasAlpha, dither );
+ }
+
+ delete[] rgbaData;
+
+ UpdateImageDataDontTouchMipmap();
+ #endif //GFX_SUPPORTS_DXT_COMPRESSION
+}
+
+#if UNITY_EDITOR
+void Texture2D::WarnInstantiateDisallowed ()
+{
+ if (!m_IsReadable)
+ {
+ ErrorStringObject(Format("Instantiating a non-readable '%s' texture is not allowed! Please mark the texture readable in the inspector or don't instantiate it.", GetName()), this);
+ }
+}
+
+bool Texture2D::IgnoreMasterTextureLimit () const
+{
+ return m_IgnoreMasterTextureLimit;
+}
+
+void Texture2D::SetIgnoreMasterTextureLimit (bool ignore)
+{
+ m_IgnoreMasterTextureLimit = ignore;
+}
+
+bool Texture2D::GetAlphaIsTransparency() const
+{
+ return m_AlphaIsTransparency;
+}
+
+void Texture2D::SetAlphaIsTransparency(bool is)
+{
+ m_AlphaIsTransparency = is;
+}
+#endif // UNITY_EDITOR
+
+
+void Texture2D::Apply(bool updateMipmaps, bool makeNoLongerReadable)
+{
+ if( makeNoLongerReadable )
+ {
+ SetIsReadable(false);
+ SetIsUnreloadable(true);
+ }
+
+ if( IsAnyCompressedTextureFormat(GetTextureFormat()) )
+ updateMipmaps = false;
+
+ if (updateMipmaps) UpdateImageData();
+ else UpdateImageDataDontTouchMipmap();
+}
+
+