2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
12 #include "ServiceBroker.h"
14 #include "commons/ilog.h"
15 #include "filesystem/File.h"
16 #include "filesystem/ResourceFile.h"
17 #include "filesystem/XbtFile.h"
18 #include "guilib/TextureBase.h"
19 #include "guilib/TextureFormats.h"
20 #include "guilib/iimage.h"
21 #include "guilib/imagefactory.h"
22 #include "messaging/ApplicationMessenger.h"
23 #include "utils/URIUtils.h"
24 #include "utils/log.h"
25 #include "windowing/WinSystem.h"
26 #if defined(TARGET_DARWIN_EMBEDDED)
27 #include <ImageIO/ImageIO.h>
28 #include "filesystem/File.h"
30 #if defined(TARGET_ANDROID)
31 #include "platform/android/filesystem/AndroidAppFile.h"
33 #include "rendering/RenderSystem.h"
34 #include "utils/MemUtils.h"
41 /************************************************************************/
43 /************************************************************************/
44 CTexture::CTexture(unsigned int width
, unsigned int height
, XB_FMT format
)
47 m_loadedToGPU
= false;
48 Allocate(width
, height
, format
);
53 KODI::MEMORY::AlignedFree(m_pixels
);
57 void CTexture::Update(unsigned int width
,
61 const unsigned char* pixels
,
67 if (format
& XB_FMT_DXT_MASK
)
70 Allocate(width
, height
, format
);
72 if (m_pixels
== nullptr)
75 unsigned int srcPitch
= pitch
? pitch
: GetPitch(width
);
76 unsigned int srcRows
= GetRows(height
);
77 unsigned int dstPitch
= GetPitch(m_textureWidth
);
78 unsigned int dstRows
= GetRows(m_textureHeight
);
80 if (srcPitch
== dstPitch
)
81 memcpy(m_pixels
, pixels
, srcPitch
* std::min(srcRows
, dstRows
));
84 const unsigned char *src
= pixels
;
85 unsigned char* dst
= m_pixels
;
86 for (unsigned int y
= 0; y
< srcRows
&& y
< dstRows
; y
++)
88 memcpy(dst
, src
, std::min(srcPitch
, dstPitch
));
99 std::unique_ptr
<CTexture
> CTexture::LoadFromFile(const std::string
& texturePath
,
100 unsigned int idealWidth
,
101 unsigned int idealHeight
,
103 const std::string
& strMimeType
)
105 #if defined(TARGET_ANDROID)
106 CURL
url(texturePath
);
107 if (url
.IsProtocol("androidapp"))
109 XFILE::CFileAndroidApp file
;
112 unsigned char* inputBuff
;
115 unsigned int inputBuffSize
= file
.ReadIcon(&inputBuff
, &width
, &height
);
120 std::unique_ptr
<CTexture
> texture
= CTexture::CreateTexture();
121 texture
->LoadFromMemory(width
, height
, width
*4, XB_FMT_RGBA8
, true, inputBuff
);
127 std::unique_ptr
<CTexture
> texture
= CTexture::CreateTexture();
128 if (texture
->LoadFromFileInternal(texturePath
, idealWidth
, idealHeight
, requirePixels
, strMimeType
))
133 std::unique_ptr
<CTexture
> CTexture::LoadFromFileInMemory(unsigned char* buffer
,
135 const std::string
& mimeType
,
136 unsigned int idealWidth
,
137 unsigned int idealHeight
)
139 std::unique_ptr
<CTexture
> texture
= CTexture::CreateTexture();
140 if (texture
->LoadFromFileInMem(buffer
, bufferSize
, mimeType
, idealWidth
, idealHeight
))
145 bool CTexture::LoadFromFileInternal(const std::string
& texturePath
,
146 unsigned int maxWidth
,
147 unsigned int maxHeight
,
149 const std::string
& strMimeType
)
151 if (URIUtils::HasExtension(texturePath
, ".dds"))
152 { // special case for DDS images
154 if (image
.ReadFile(texturePath
))
156 Update(image
.GetWidth(), image
.GetHeight(), 0, image
.GetFormat(), image
.GetData(), false);
162 unsigned int width
= maxWidth
? std::min(maxWidth
, CServiceBroker::GetRenderSystem()->GetMaxTextureSize()) :
163 CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
164 unsigned int height
= maxHeight
? std::min(maxHeight
, CServiceBroker::GetRenderSystem()->GetMaxTextureSize()) :
165 CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
167 // Read image into memory to use our vfs
169 std::vector
<uint8_t> buf
;
171 if (file
.LoadFile(texturePath
, buf
) <= 0)
174 CURL
url(texturePath
);
175 // make sure resource:// paths are properly resolved
176 if (url
.IsProtocol("resource"))
178 std::string translatedPath
;
179 if (XFILE::CResourceFile::TranslatePath(url
, translatedPath
))
180 url
.Parse(translatedPath
);
183 // handle xbt:// paths differently because it allows loading the texture directly from memory
184 if (url
.IsProtocol("xbt"))
186 XFILE::CXbtFile xbtFile
;
187 if (!xbtFile
.Open(url
))
189 if (xbtFile
.GetKDFormatType())
191 return UploadFromMemory(xbtFile
.GetImageWidth(), xbtFile
.GetImageHeight(), 0, buf
.data(),
192 xbtFile
.GetKDFormat(), xbtFile
.GetKDAlpha(), xbtFile
.GetKDSwizzle());
194 else if (xbtFile
.GetImageFormat() == XB_FMT_A8R8G8B8
)
196 KD_TEX_ALPHA alpha
= xbtFile
.HasImageAlpha() ? KD_TEX_ALPHA_STRAIGHT
: KD_TEX_ALPHA_OPAQUE
;
197 return UploadFromMemory(xbtFile
.GetImageWidth(), xbtFile
.GetImageHeight(), 0, buf
.data(),
198 KD_TEX_FMT_SDR_BGRA8
, alpha
, KD_TEX_SWIZ_RGBA
);
208 if(strMimeType
.empty())
209 pImage
= ImageFactory::CreateLoader(texturePath
);
211 pImage
= ImageFactory::CreateLoaderFromMimeType(strMimeType
);
213 if (!LoadIImage(pImage
, buf
.data(), buf
.size(), width
, height
))
215 CLog::Log(LOGDEBUG
, "{} - Load of {} failed.", __FUNCTION__
, CURL::GetRedacted(texturePath
));
224 bool CTexture::LoadFromFileInMem(unsigned char* buffer
,
226 const std::string
& mimeType
,
227 unsigned int maxWidth
,
228 unsigned int maxHeight
)
230 if (!buffer
|| !size
)
233 unsigned int width
= maxWidth
? std::min(maxWidth
, CServiceBroker::GetRenderSystem()->GetMaxTextureSize()) :
234 CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
235 unsigned int height
= maxHeight
? std::min(maxHeight
, CServiceBroker::GetRenderSystem()->GetMaxTextureSize()) :
236 CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
238 IImage
* pImage
= ImageFactory::CreateLoaderFromMimeType(mimeType
);
239 if(!LoadIImage(pImage
, buffer
, size
, width
, height
))
248 bool CTexture::LoadIImage(IImage
* pImage
,
249 unsigned char* buffer
,
250 unsigned int bufSize
,
254 if (pImage
== nullptr)
257 if (!pImage
->LoadImageFromMemory(buffer
, bufSize
, width
, height
))
260 if (pImage
->Width() == 0 || pImage
->Height() == 0)
263 // align all textures so that they have an even width
264 // in some circumstances when we downsize a thumbnail
265 // which has an uneven number of pixels in width
266 // we crash in CPicture::ScaleImage in ffmpegs swscale
267 // because it tries to access beyond the source memory
268 // (happens on osx and ios)
269 // UPDATE: don't just update to be on an even width;
270 // ffmpegs swscale relies on a 16-byte stride on some systems
271 // so the textureWidth needs to be a multiple of 16. see ffmpeg
272 // swscale headers for more info.
273 unsigned int textureWidth
= ((pImage
->Width() + 15) / 16) * 16;
275 Allocate(textureWidth
, pImage
->Height(), XB_FMT_A8R8G8B8
);
277 m_imageWidth
= std::min(m_imageWidth
, textureWidth
);
279 if (m_pixels
== nullptr)
282 if (!pImage
->Decode(m_pixels
, GetTextureWidth(), GetRows(), GetPitch(), XB_FMT_A8R8G8B8
))
285 if (pImage
->Orientation())
286 m_orientation
= pImage
->Orientation() - 1;
288 m_textureAlpha
= pImage
->hasAlpha() ? KD_TEX_ALPHA_STRAIGHT
: KD_TEX_ALPHA_OPAQUE
;
289 m_originalWidth
= pImage
->originalWidth();
290 m_originalHeight
= pImage
->originalHeight();
291 m_imageWidth
= pImage
->Width();
292 m_imageHeight
= pImage
->Height();
299 void CTexture::LoadToGPUAsync()
301 // Already in main context?
302 if (CServiceBroker::GetWinSystem()->HasContext())
305 if (!CServiceBroker::GetWinSystem()->BindTextureUploadContext())
312 CServiceBroker::GetWinSystem()->UnbindTextureUploadContext();
315 bool CTexture::LoadFromMemory(unsigned int width
,
320 const unsigned char* pixels
)
322 m_imageWidth
= m_originalWidth
= width
;
323 m_imageHeight
= m_originalHeight
= height
;
325 m_textureAlpha
= hasAlpha
? KD_TEX_ALPHA_STRAIGHT
: KD_TEX_ALPHA_OPAQUE
;
326 Update(width
, height
, pitch
, format
, pixels
, false);
330 bool CTexture::UploadFromMemory(unsigned int width
,
333 unsigned char* pixels
,
338 m_imageWidth
= m_textureWidth
= m_originalWidth
= width
;
339 m_imageHeight
= m_textureHeight
= m_originalHeight
= height
;
340 m_textureFormat
= format
;
341 m_textureAlpha
= alpha
;
342 m_textureSwizzle
= swizzle
;
344 if (!SupportsFormat(m_textureFormat
, m_textureSwizzle
) && !ConvertToLegacy(width
, height
, pixels
))
348 "Failed to upload texture. Format {} and swizzle {} not supported by the texture pipeline.",
349 m_textureFormat
, m_textureSwizzle
);
351 m_loadedToGPU
= true;
355 if (CServiceBroker::GetAppMessenger()->IsProcessThread())
363 // just a borrowed buffer
365 m_bCacheMemory
= true;
367 m_bCacheMemory
= false;
373 size_t size
= GetPitch() * GetRows();
374 m_pixels
= static_cast<unsigned char*>(KODI::MEMORY::AlignedMalloc(size
, 32));
375 if (m_pixels
== nullptr)
377 CLog::LogF(LOGERROR
, "Could not allocate {} bytes. Out of memory.", size
);
380 std::memcpy(m_pixels
, pixels
, size
);
386 bool CTexture::LoadPaletted(unsigned int width
,
390 const unsigned char* pixels
,
391 const COLOR
* palette
)
393 if (pixels
== NULL
|| palette
== NULL
)
396 Allocate(width
, height
, format
);
398 for (unsigned int y
= 0; y
< m_imageHeight
; y
++)
400 unsigned char *dest
= m_pixels
+ y
* GetPitch();
401 const unsigned char *src
= pixels
+ y
* pitch
;
402 for (unsigned int x
= 0; x
< m_imageWidth
; x
++)
404 COLOR col
= palette
[*src
++];