1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "IrrCompileConfig.h"
6 #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_
7 
8 #define _IRR_DONT_DO_MEMORY_DEBUGGING_HERE
9 #include "CD3D8Texture.h"
10 #include "CD3D8Driver.h"
11 #include "os.h"
12 
13 
14 #ifndef _IRR_COMPILE_WITH_DIRECT3D_9_
15 // The D3DXFilterTexture function seems to get linked wrong when
16 // compiling with both D3D8 and 9, causing it not to work in the D3D9 device.
17 // So mipmapgeneration is replaced with my own bad generation in d3d 8 when
18 // compiling with both D3D 8 and 9.
19 //#define _IRR_USE_D3DXFilterTexture_
20 #endif // _IRR_COMPILE_WITH_DIRECT3D_9_
21 
22 #include <d3dx8tex.h>
23 
24 #ifdef _IRR_USE_D3DXFilterTexture_
25 #pragma comment (lib, "d3dx8.lib")
26 #endif // _IRR_USE_D3DXFilterTexture_
27 
28 namespace irr
29 {
30 namespace video
31 {
32 
33 //! rendertarget constructor
CD3D8Texture(CD3D8Driver * driver,const core::dimension2d<u32> & size,const io::path & name)34 CD3D8Texture::CD3D8Texture(CD3D8Driver* driver, const core::dimension2d<u32>& size, const io::path& name)
35 : ITexture(name), Texture(0), RTTSurface(0), Driver(driver),
36 	TextureSize(size), ImageSize(size), Pitch(0),
37 	HasMipMaps(false), IsRenderTarget(true)
38 {
39 	#ifdef _DEBUG
40 	setDebugName("CD3D8Texture");
41 	#endif
42 
43 	Device=driver->getExposedVideoData().D3D8.D3DDev8;
44 	if (Device)
45 		Device->AddRef();
46 
47 	createRenderTarget();
48 }
49 
50 
51 //! constructor
CD3D8Texture(IImage * image,CD3D8Driver * driver,u32 flags,const io::path & name,void * mipmapData)52 CD3D8Texture::CD3D8Texture(IImage* image, CD3D8Driver* driver,
53 			u32 flags, const io::path& name, void* mipmapData)
54 : ITexture(name), Texture(0), RTTSurface(0), Driver(driver),
55 TextureSize(0,0), ImageSize(0,0), Pitch(0),
56 HasMipMaps(false), IsRenderTarget(false)
57 {
58 	#ifdef _DEBUG
59 	setDebugName("CD3D8Texture");
60 	#endif
61 
62 	HasMipMaps = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
63 
64 	Device=driver->getExposedVideoData().D3D8.D3DDev8;
65 	if (Device)
66 		Device->AddRef();
67 
68 	if (image)
69 	{
70 		if (createTexture(flags, image))
71 		{
72 			if (copyTexture(image))
73 			{
74 				regenerateMipMapLevels(mipmapData);
75 			}
76 		}
77 		else
78 			os::Printer::log("Could not create DIRECT3D8 Texture.", ELL_WARNING);
79 	}
80 }
81 
82 
83 //! destructor
~CD3D8Texture()84 CD3D8Texture::~CD3D8Texture()
85 {
86 	if (Texture)
87 		Texture->Release();
88 
89 	if (RTTSurface)
90 		RTTSurface->Release();
91 
92 	if (Device)
93 		Device->Release();
94 }
95 
96 
97 //! creates the hardware texture
createTexture(u32 flags,video::IImage * image)98 bool CD3D8Texture::createTexture(u32 flags, video::IImage* image)
99 {
100 	ImageSize = image->getDimension();
101 
102 	core::dimension2d<u32> optSize = ImageSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT), !Driver->queryFeature(EVDF_TEXTURE_NSQUARE), true, Driver->Caps.MaxTextureWidth);
103 
104 	D3DFORMAT format = D3DFMT_A1R5G5B5;
105 	switch(getTextureFormatFromFlags(flags))
106 	{
107 	case ETCF_ALWAYS_16_BIT:
108 		format = D3DFMT_A1R5G5B5; break;
109 	case ETCF_ALWAYS_32_BIT:
110 		format = D3DFMT_A8R8G8B8; break;
111 	case ETCF_OPTIMIZED_FOR_QUALITY:
112 		{
113 			switch(image->getColorFormat())
114 			{
115 			case ECF_R8G8B8:
116 			case ECF_A8R8G8B8:
117 				format = D3DFMT_A8R8G8B8; break;
118 			case ECF_A1R5G5B5:
119 			case ECF_R5G6B5:
120 				format = D3DFMT_A1R5G5B5; break;
121 			}
122 		}
123 		break;
124 	case ETCF_OPTIMIZED_FOR_SPEED:
125 		format = D3DFMT_A1R5G5B5; break;
126 	}
127 
128 	if (Driver->getTextureCreationFlag(video::ETCF_NO_ALPHA_CHANNEL))
129 	{
130 		if (format == D3DFMT_A8R8G8B8)
131 
132 #ifdef _IRR_XBOX_PLATFORM_
133 			format = D3DFMT_X8R8G8B8;
134 #else
135 			format = D3DFMT_R8G8B8;
136 #endif
137 
138 		else if (format == D3DFMT_A1R5G5B5)
139 			format = D3DFMT_R5G6B5;
140 	}
141 
142 	const bool mipmaps = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
143 
144 	HRESULT hr = Device->CreateTexture(optSize.Width, optSize.Height,
145 		mipmaps ? 0 : 1, // number of mipmaplevels (0 = automatic all)
146 		0, format, D3DPOOL_MANAGED, &Texture);
147 
148 	if (FAILED(hr))
149 	{
150 		// try brute force 16 bit
151 		if (format == D3DFMT_A8R8G8B8)
152 			format = D3DFMT_A1R5G5B5;
153 #ifdef _IRR_XBOX_PLATFORM_
154 		else if (format == D3DFMT_X8R8G8B8)
155 			format = D3DFMT_R5G6B5;
156 #else
157 		else if (format == D3DFMT_R8G8B8)
158 			format = D3DFMT_R5G6B5;
159 #endif
160 		else
161 			return false;
162 
163 		hr = Device->CreateTexture(optSize.Width, optSize.Height,
164 			mipmaps ? 0 : 1, // number of mipmaplevels (0 = automatic all)
165 			0, format, D3DPOOL_MANAGED, &Texture);
166 	}
167 
168 	ColorFormat = getColorFormatFromD3DFormat(format);
169 	return (SUCCEEDED(hr));
170 }
171 
172 
173 //! copies the image to the texture
copyTexture(video::IImage * image)174 bool CD3D8Texture::copyTexture(video::IImage* image)
175 {
176 	if (Texture && image)
177 	{
178 		D3DSURFACE_DESC desc;
179 		Texture->GetLevelDesc(0, &desc);
180 
181 		TextureSize.Width = desc.Width;
182 		TextureSize.Height = desc.Height;
183 
184 		D3DLOCKED_RECT rect;
185 		HRESULT hr = Texture->LockRect(0, &rect, 0, 0);
186 		if (FAILED(hr))
187 		{
188 			os::Printer::log("Could not lock D3D8 Texture.", ELL_ERROR);
189 			return false;
190 		}
191 
192 		Pitch = rect.Pitch;
193 		image->copyToScaling(rect.pBits, TextureSize.Width, TextureSize.Height, ColorFormat, Pitch);
194 
195 		hr = Texture->UnlockRect(0);
196 		if (FAILED(hr))
197 		{
198 			os::Printer::log("Could not unlock D3D8 Texture.", ELL_ERROR);
199 			return false;
200 		}
201 	}
202 
203 	return true;
204 }
205 
206 
207 //! lock function
lock(E_TEXTURE_LOCK_MODE mode,u32 mipmapLevel)208 void* CD3D8Texture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel)
209 {
210 	if (!Texture)
211 		return 0;
212 
213 	MipLevelLocked=mipmapLevel;
214 	HRESULT hr;
215 	D3DLOCKED_RECT rect;
216 	if(!IsRenderTarget)
217 	{
218 		hr = Texture->LockRect(mipmapLevel, &rect, 0, (mode==ETLM_READ_ONLY)?D3DLOCK_READONLY:0);
219 		if (FAILED(hr))
220 		{
221 			os::Printer::log("Could not lock DIRECT3D9 Texture.", ELL_ERROR);
222 			return 0;
223 		}
224 	}
225 	else
226 	{
227 		if (!RTTSurface)
228 		{
229 			// Make RTT surface large enough for all miplevels (including 0)
230 			D3DSURFACE_DESC desc;
231 			Texture->GetLevelDesc(0, &desc);
232 			hr = Device->CreateImageSurface(desc.Width, desc.Height, desc.Format, &RTTSurface);
233 			if (FAILED(hr))
234 			{
235 				os::Printer::log("Could not lock DIRECT3D8 Texture.", ELL_ERROR);
236 				return 0;
237 			}
238 		}
239 
240 		IDirect3DSurface8 *surface = 0;
241 		hr = Texture->GetSurfaceLevel(mipmapLevel, &surface);
242 		if (FAILED(hr))
243 		{
244 			os::Printer::log("Could not lock DIRECT3D8 Texture.", "Could not get surface.", ELL_ERROR);
245 			return 0;
246 		}
247 		hr = Device->CopyRects(surface, 0, 0, RTTSurface, 0);
248 		surface->Release();
249 		if(FAILED(hr))
250 		{
251 			os::Printer::log("Could not lock DIRECT3D8 Texture.", "Data copy failed.", ELL_ERROR);
252 			return 0;
253 		}
254 		hr = RTTSurface->LockRect(&rect, 0, (mode==ETLM_READ_ONLY)?D3DLOCK_READONLY:0);
255 		if(FAILED(hr))
256 		{
257 			os::Printer::log("Could not lock DIRECT3D8 Texture.", "LockRect failed.", ELL_ERROR);
258 			return 0;
259 		}
260 	}
261 	return rect.pBits;
262 }
263 
264 
265 //! unlock function
unlock()266 void CD3D8Texture::unlock()
267 {
268 	if (!Texture)
269 		return;
270 
271 	if (!IsRenderTarget)
272 		Texture->UnlockRect(MipLevelLocked);
273 	else if (RTTSurface)
274 		RTTSurface->UnlockRect();
275 }
276 
277 
278 //! Returns original size of the texture.
getOriginalSize() const279 const core::dimension2d<u32>& CD3D8Texture::getOriginalSize() const
280 {
281 	return ImageSize;
282 }
283 
284 
285 //! Returns (=size) of the texture.
getSize() const286 const core::dimension2d<u32>& CD3D8Texture::getSize() const
287 {
288 	return TextureSize;
289 }
290 
291 
292 //! returns driver type of texture (=the driver, who created the texture)
getDriverType() const293 E_DRIVER_TYPE CD3D8Texture::getDriverType() const
294 {
295 	return EDT_DIRECT3D8;
296 }
297 
298 
299 //! returns color format of texture
getColorFormat() const300 ECOLOR_FORMAT CD3D8Texture::getColorFormat() const
301 {
302 	return ColorFormat;
303 }
304 
305 
306 //! returns pitch of texture (in bytes)
getPitch() const307 u32 CD3D8Texture::getPitch() const
308 {
309 	return Pitch;
310 }
311 
312 
313 //! returns the DIRECT3D8 Texture
getDX8Texture() const314 IDirect3DTexture8* CD3D8Texture::getDX8Texture() const
315 {
316 	return Texture;
317 }
318 
319 
320 //! returns if texture has mipmap levels
hasMipMaps() const321 bool CD3D8Texture::hasMipMaps() const
322 {
323 	return HasMipMaps;
324 }
325 
326 
327 // The D3DXFilterTexture function seems to get linked wrong when
328 // compiling with both D3D8 and 9, causing it not to work in the D3D9 device.
329 // So mipmapgeneration is replaced with my own bad generation in d3d 8 when
330 // compiling with both D3D 8 and 9.
createMipMaps(u32 level)331 bool CD3D8Texture::createMipMaps(u32 level)
332 {
333 	if (level==0)
334 		return true;
335 
336 	IDirect3DSurface8* upperSurface = 0;
337 	IDirect3DSurface8* lowerSurface = 0;
338 
339 	// get upper level
340 	HRESULT hr = Texture->GetSurfaceLevel(level-1, &upperSurface);
341 	if (FAILED(hr) || !upperSurface)
342 	{
343 		os::Printer::log("Could not get upper surface level for mip map generation", ELL_WARNING);
344 		return false;
345 	}
346 
347 	// get lower level
348 	hr = Texture->GetSurfaceLevel(level, &lowerSurface);
349 	if (FAILED(hr) || !lowerSurface)
350 	{
351 		os::Printer::log("Could not get lower surface level for mip map generation", ELL_WARNING);
352 		upperSurface->Release();
353 		return false;
354 	}
355 
356 	D3DSURFACE_DESC upperDesc, lowerDesc;
357 	upperSurface->GetDesc(&upperDesc);
358 	lowerSurface->GetDesc(&lowerDesc);
359 
360 	D3DLOCKED_RECT upperlr;
361 	D3DLOCKED_RECT lowerlr;
362 
363 	// lock upper surface
364 	if (FAILED(upperSurface->LockRect(&upperlr, NULL, 0)))
365 	{
366 		os::Printer::log("Could not lock upper texture for mip map generation", ELL_WARNING);
367 		upperSurface->Release();
368 		lowerSurface->Release();
369 		return false;
370 	}
371 
372 	// lock lower surface
373 	if (FAILED(lowerSurface->LockRect(&lowerlr, NULL, 0)))
374 	{
375 		os::Printer::log("Could not lock lower texture for mip map generation", ELL_WARNING);
376 		upperSurface->UnlockRect();
377 		upperSurface->Release();
378 		lowerSurface->Release();
379 		return false;
380 	}
381 
382 	if (upperDesc.Format != lowerDesc.Format)
383 	{
384 		os::Printer::log("Cannot copy mip maps with different formats.", ELL_WARNING);
385 	}
386 	else
387 	{
388 		if (upperDesc.Format == D3DFMT_A1R5G5B5)
389 			copy16BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits,
390 							lowerDesc.Width, lowerDesc.Height,
391 							upperlr.Pitch, lowerlr.Pitch);
392 		else
393 		if (upperDesc.Format == D3DFMT_A8R8G8B8)
394 			copy32BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits,
395 							lowerDesc.Width, lowerDesc.Height,
396 							upperlr.Pitch, lowerlr.Pitch);
397 		else
398 			os::Printer::log("Unsupported mipmap format, cannot copy.", ELL_WARNING);
399 	}
400 
401 	bool result=true;
402 	// unlock
403 	if (FAILED(upperSurface->UnlockRect()))
404 		result=false;
405 	if (FAILED(lowerSurface->UnlockRect()))
406 		result=false;
407 
408 	// release
409 	upperSurface->Release();
410 	lowerSurface->Release();
411 
412 	if (!result || upperDesc.Width < 3 || upperDesc.Height < 3)
413 		return result; // stop generating levels
414 
415 	// generate next level
416 	return createMipMaps(level+1);
417 }
418 
419 
getColorFormatFromD3DFormat(D3DFORMAT format)420 ECOLOR_FORMAT CD3D8Texture::getColorFormatFromD3DFormat(D3DFORMAT format)
421 {
422 	switch(format)
423 	{
424 	case D3DFMT_X1R5G5B5:
425 	case D3DFMT_A1R5G5B5:
426 		Pitch = TextureSize.Width * 2;
427 		return ECF_A1R5G5B5;
428 		break;
429 	case D3DFMT_A8R8G8B8:
430 	case D3DFMT_X8R8G8B8:
431 		Pitch = TextureSize.Width * 4;
432 		return ECF_A8R8G8B8;
433 		break;
434 	case D3DFMT_R5G6B5:
435 		Pitch = TextureSize.Width * 2;
436 		return ECF_R5G6B5;
437 		break;
438 	default:
439 		return (ECOLOR_FORMAT)0;
440 	};
441 }
442 
443 
444 
copy16BitMipMap(char * src,char * tgt,s32 width,s32 height,s32 pitchsrc,s32 pitchtgt) const445 void CD3D8Texture::copy16BitMipMap(char* src, char* tgt,
446 				   s32 width, s32 height,
447 				   s32 pitchsrc, s32 pitchtgt) const
448 {
449 	u16 c;
450 
451 	for (int x=0; x<width; ++x)
452 	{
453 		for (int y=0; y<height; ++y)
454 		{
455 			s32 a=0, r=0, g=0, b=0;
456 
457 			for (int dx=0; dx<2; ++dx)
458 			{
459 				for (int dy=0; dy<2; ++dy)
460 				{
461 					int tgx = (x*2)+dx;
462 					int tgy = (y*2)+dy;
463 
464 					c = *(u16*)((void*)&src[(tgx*2)+(tgy*pitchsrc)]);
465 
466 					a += getAlpha(c);
467 					r += getRed(c);
468 					g += getGreen(c);
469 					b += getBlue(c);
470 				}
471 			}
472 
473 			a /= 4;
474 			r /= 4;
475 			g /= 4;
476 			b /= 4;
477 
478 			c = ((a & 0x1) <<15) | ((r & 0x1F)<<10) | ((g & 0x1F)<<5) | (b & 0x1F);
479 			*(u16*)((void*)&tgt[(x*2)+(y*pitchtgt)]) = c;
480 		}
481 	}
482 }
483 
484 
copy32BitMipMap(char * src,char * tgt,s32 width,s32 height,s32 pitchsrc,s32 pitchtgt) const485 void CD3D8Texture::copy32BitMipMap(char* src, char* tgt,
486 				   s32 width, s32 height,
487 				   s32 pitchsrc, s32 pitchtgt) const
488 {
489 	SColor c;
490 
491 	for (int x=0; x<width; ++x)
492 	{
493 		for (int y=0; y<height; ++y)
494 		{
495 			s32 a=0, r=0, g=0, b=0;
496 
497 			for (int dx=0; dx<2; ++dx)
498 			{
499 				for (int dy=0; dy<2; ++dy)
500 				{
501 					int tgx = (x*2)+dx;
502 					int tgy = (y*2)+dy;
503 
504 					c = *(u32*)((void*)&src[(tgx<<2)+(tgy*pitchsrc)]);
505 
506 					a += c.getAlpha();
507 					r += c.getRed();
508 					g += c.getGreen();
509 					b += c.getBlue();
510 				}
511 			}
512 
513 			a >>= 2;
514 			r >>= 2;
515 			g >>= 2;
516 			b >>= 2;
517 
518 			c = ((a & 0xff)<<24) | ((r & 0xff)<<16) | ((g & 0xff)<<8) | (b & 0xff);
519 			*(u32*)((void*)&tgt[(x*4)+(y*pitchtgt)]) = c.color;
520 		}
521 	}
522 }
523 
524 
createRenderTarget()525 void CD3D8Texture::createRenderTarget()
526 {
527 	TextureSize = TextureSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT), !Driver->queryFeature(EVDF_TEXTURE_NSQUARE), true, Driver->Caps.MaxTextureWidth);
528 
529 	// get backbuffer format to create the render target in the
530 	// same format
531 
532 	IDirect3DSurface8* bb;
533 	D3DFORMAT d3DFormat = D3DFMT_A8R8G8B8;
534 
535 	if (!FAILED(Device->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &bb)))
536 	{
537 		D3DSURFACE_DESC desc;
538 		bb->GetDesc(&desc);
539 		d3DFormat = desc.Format;
540 
541 		if (d3DFormat == D3DFMT_X8R8G8B8)
542 			d3DFormat = D3DFMT_A8R8G8B8;
543 
544 		bb->Release();
545 	}
546 	else
547 	{
548 		os::Printer::log("Could not create RenderTarget texture: could not get BackBuffer.",
549 			ELL_WARNING);
550 		return;
551 	}
552 
553 	// create texture
554 	HRESULT hr;
555 
556 	hr = Device->CreateTexture(
557 		TextureSize.Width,
558 		TextureSize.Height,
559 		1, // mip map level count, we don't want mipmaps here
560 		D3DUSAGE_RENDERTARGET,
561 		d3DFormat,
562 		D3DPOOL_DEFAULT,
563 		&Texture);
564 
565 	// get irrlicht format from D3D format
566 	ColorFormat = getColorFormatFromD3DFormat(d3DFormat);
567 
568 	if (FAILED(hr))
569 		os::Printer::log("Could not create render target texture");
570 }
571 
572 
573 //! Regenerates the mip map levels of the texture. Useful after locking and
574 //! modifying the texture
regenerateMipMapLevels(void * mipmapData)575 void CD3D8Texture::regenerateMipMapLevels(void* mipmapData)
576 {
577 	if (mipmapData)
578 	{
579 		core::dimension2du size = TextureSize;
580 		u32 level=0;
581 		do
582 		{
583 			if (size.Width>1)
584 				size.Width /=2;
585 			if (size.Height>1)
586 				size.Height /=2;
587 			++level;
588 			IDirect3DSurface8* mipSurface = 0;
589 			HRESULT hr = Texture->GetSurfaceLevel(level, &mipSurface);
590 			if (FAILED(hr) || !mipSurface)
591 			{
592 				os::Printer::log("Could not get mipmap level", ELL_WARNING);
593 				return;
594 			}
595 			D3DSURFACE_DESC mipDesc;
596 			mipSurface->GetDesc(&mipDesc);
597 			D3DLOCKED_RECT miplr;
598 
599 			// lock mipmap surface
600 			if (FAILED(mipSurface->LockRect(&miplr, NULL, 0)))
601 			{
602 				mipSurface->Release();
603 				os::Printer::log("Could not lock texture", ELL_WARNING);
604 				return;
605 			}
606 
607 			memcpy(miplr.pBits, mipmapData, size.getArea()*getPitch()/TextureSize.Width);
608 			mipmapData = (u8*)mipmapData+size.getArea()*getPitch()/TextureSize.Width;
609 			// unlock
610 			mipSurface->UnlockRect();
611 			// release
612 			mipSurface->Release();
613 		} while (size.Width != 1 || size.Height != 1);
614 	}
615 	else if (HasMipMaps)
616 	{
617 		// create mip maps.
618 #ifndef _IRR_USE_D3DXFilterTexture_
619 		// The D3DXFilterTexture function seems to get linked wrong when
620 		// compiling with both D3D8 and 9, causing it not to work in the D3D9 device.
621 		// So mipmapgeneration is replaced with my own bad generation in d3d 8 when
622 		// compiling with both D3D 8 and 9.
623 		HRESULT hr  = D3DXFilterTexture(Texture, NULL, D3DX_DEFAULT , D3DX_DEFAULT );
624 		if (FAILED(hr))
625 #endif
626 		createMipMaps();
627 	}
628 }
629 
630 
631 //! returns if it is a render target
isRenderTarget() const632 bool CD3D8Texture::isRenderTarget() const
633 {
634 	return IsRenderTarget;
635 }
636 
637 
638 //! Returns pointer to the render target surface
getRenderTargetSurface()639 IDirect3DSurface8* CD3D8Texture::getRenderTargetSurface()
640 {
641 	if (!IsRenderTarget)
642 		return 0;
643 
644 	IDirect3DSurface8 *pRTTSurface = 0;
645 	if (Texture)
646 		Texture->GetSurfaceLevel(0, &pRTTSurface);
647 
648 	if (pRTTSurface)
649 		pRTTSurface->Release();
650 
651 	return pRTTSurface;
652 }
653 
654 
655 } // end namespace video
656 } // end namespace irr
657 
658 #endif // _IRR_COMPILE_WITH_DIRECT3D_8_
659 
660