CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 3 123 LastLast
Results 1 to 15 of 31

Hybrid View

  1. #1
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    [RESOLVED] Draw Image Using 2D Byte Array ?

    Hi,

    I like to Draw an Image Using two dimensional byte array[512][256].

    I used SetPixelV method to plot the image.

    But Its very slow & hidden some buttons(Controls) at run time in the same dialog.

    For reference i send some code,

    Code:
    for(row = 0; row <512; row ++)
    { 
      for(col = 0; col < 256; col++)
      {
        Data[row][col] = y;
      
       SetPixelV(d, 10+row, 10+col, RGB(Data[row][col],Data[row][col],Data[row][col]));
      }
    }
    pls, clear me a better solution.
    Regards,

    SaraswathiSrinath

  2. #2
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    I'm helpless now. Any body please help me.

    This is heart of my project.
    Regards,

    SaraswathiSrinath

  3. #3
    Join Date
    Apr 2000
    Location
    Belgium (Europe)
    Posts
    4,626

    Re: Draw Image Using 2D Byte Array ?

    Setpixel calls is slow, it's a bad way to do pretty much anything.

    what you want to do is create a memory bitmap, and then access the bytes of the bitmap directly.
    Have a look at CreateDIBSection() and/or GetDIBits() on MSDN

  4. #4
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    What abt BitBlt method?
    Regards,

    SaraswathiSrinath

  5. #5
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,620

    Re: Draw Image Using 2D Byte Array ?

    BitBlt function

    The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context.
    To make it short, BitBlt transfers source DC bitmap bits to destination DC. Which means, you must have an already formed bitmap in source DC prior to blitting one.

    Yet again, refer to CreateDIBSection


    A DIBSection wrapper for Win32 and WinCE
    By Chris Maunder, 22 May 2001
    Best regards,
    Igor

  6. #6
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    Thanks a lot Mr.OReubens & Mr.Igor . I will study the above link and come back.
    Regards,

    SaraswathiSrinath

  7. #7
    Join Date
    Feb 2013
    Location
    United States
    Posts
    56

    Re: Draw Image Using 2D Byte Array ?

    Oh the agony of SetPixel()! I used it too, back when I did not know any better. It took about 1200 clock cycles to plot one pixel!

    Like it was mentioned above, you need to call CreateDIBSection() to create a bitmap object. CreateDIBSection() allocates a one dimensional array to hold the image data. In the following code samples below, I have created my own structures that have more meaningful attribute names. You should be able to figure out what the official attribute names are. Also, the variable ImageData points to an array of unsigned ints.

    Code:
    // Create a bitmap object.
    DeviceContextHandle = NULL;
    ImageData = NULL;
    NumberOfDoubleWordsPerImageRow = ((ImageWidth + 1) * 3) >> 2;
    BitmapHeader.HeaderSize = sizeof(bitmap_header);
    BitmapHeader.Width = ImageWidth;
    BitmapHeader.Height = ImageHeight;
    BitmapHeader.NumberOfPlanes = 1;
    BitmapHeader.NumberOfBitsPerPixel = 24;
    BitmapHeader.CompressionType = BI_RGB;
    BitmapHeader.ImageSize = (NumberOfDoubleWordsPerImageRow << 2) * ImageHeight;
    BitmapHeader.NumberOfHorizontalPixelsPerMeter = 0;
    BitmapHeader.NumberOfVerticalPixelsPerMeter = 0;
    BitmapHeader.NumberOfUsedPaletteColors = 0;
    BitmapHeader.NumberOfImportantPaletteColors = 0;
    WindowDeviceContextHandle = GetDC(WindowHandle);
    BitmapHandle = CreateDIBSection(WindowDeviceContextHandle, &BitmapHeader, DIB_RGB_COLORS, (void**)(&ImageData), NULL, 0);
    if (BitmapHandle != NULL)
    {
    	DeviceContextHandle = CreateCompatibleDC(WindowDeviceContextHandle);
    	if (DeviceContextHandle == NULL)
    	{
    		DeleteObject(BitmapHandle);
    	}
    	else
    	{
    		SelectObject(DeviceContextHandle, BitmapHandle);
    	}
    }
    Code:
    void SetPixel(int x, int y, unsigned int Color)
    {
    	int XDividedBy4;
    	int Index;
    
    	// Note:  Point (0, 0) is at the upper left corner of the image.
    
    	if ((x >= 0) && (x < ImageWidth) && (y >= 0) && (y < ImageHeight))
    	{
    		Color = Color & 0x00FFFFFF;
    		XDividedBy4 = x >> 2;
    		Index = ((ImageHeight - 1 - y) * NumberOfDoubleWordsPerImageRow) + XDividedBy4 + XDividedBy4 + XDividedBy4;
    		switch (x & 3)
    		{
    		case 0:
    			ImageData[Index] = (ImageData[Index] & 0xFF000000) | Color;
    			break;
    		case 1:
    			ImageData[Index] = (ImageData[Index] & 0x00FFFFFF) | (Color << 24);
    			ImageData[Index + 1] = (ImageData[Index + 1] & 0xFFFF0000) | (Color >> 8);
    			break;
    		case 2:
    			ImageData[Index + 1] = (ImageData[Index + 1] & 0x0000FFFF) | (Color << 16);
    			ImageData[Index + 2] = (ImageData[Index + 2] & 0xFFFFFF00) | (Color >> 16);
    			break;
    		case 3:
    			ImageData[Index + 2] = (ImageData[Index + 2] & 0x000000FF) | (Color << 8);
    			break;
    		}
    	}
    }
    BitBlt() copies the image data from the main memory to the graphic card's memory. This should be done when the window is repainted.
    Code:
    	case WM_PAINT:
    		WindowDeviceContextHandle = GetDC(WindowHandle);
    		BeginPaint(WindowHandle, &Paint);
    		BitBlt(Paint.DeviceContextHandle,
    		       Paint.Rectangle.Left, Paint.Rectangle.Top,
    		       Paint.Rectangle.Right - Paint.Rectangle.Left,
    		       Paint.Rectangle.Bottom - Paint.Rectangle.Top,
    		       WindowDeviceContextHandle, Paint.Rectangle.Left, Paint.Rectangle.Top, SRCCOPY);
    		EndPaint(WindowHandle, &Paint);
    I hope this clears up some of your misery.
    Coder Dave

  8. #8
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    Thank you so much Mr.Dave.

    Reading this code little difficult to understand. B'cos I'm fresher.

    Its OK I will study & try to use this code. If any doubt, I come back with you.

    If this code helps to reach my goal, really I'm very happy.

    One Again Thank you Mr.Dave.
    Regards,

    SaraswathiSrinath

  9. #9
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    hi,

    I tried the code and also felt little difficult. Here by I attached the code.

    I want to display : Bit Count = 8 + Monochrome image display.

    Code for Bit-count = 32 :
    *****************************
    Code:
    void CFrame::OnPaint()
    {
    
    // Byte Array Display
    
    CDC* pDC=GetDC();
    
    HDC hDC = *pDC;
    
    HDC hDCMem = CreateCompatibleDC(hDC);
    
    BYTE* ImgData;
    
    BITMAPINFO bi; 
    
    ZeroMemory(&bi, sizeof(BITMAPINFO));
    
    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    
    bi.bmiHeader.biWidth =512;
    
    bi.bmiHeader.biHeight = -256; //negative so (0,0) is at top left
    
    bi.bmiHeader.biPlanes = 1;
    
    bi.bmiHeader.biBitCount = 32; 
    
    HBITMAP bitmap = ::CreateDIBSection(hDCMem, &bi, DIB_RGB_COLORS, (VOID**)&ImgData, NULL, 0);
    
    HGDIOBJ oldbmp = ::SelectObject(hDCMem, bitmap); 
    
    FILE *fpCsv;
    
    if( ( fpCsv = fopen ("Blitting.txt","w")) == NULL )
    
    {
    
    MessageBox("Failed to Write Blitting.txt!","Warning",MB_OK); 
    
    return;
    
    } 
    
    int pitch = 4*512; // 4 bytes per pixel but if not 32 bit, round pitch up to multiple of 4
    
    int index;
    
    for(int x=0; x<512; x=x++)
    
    {
    
    for(int y=0; y<256; y++)
    
    {
    
    index = y * pitch; 
    
    index += x*4; 
    
    ImgData[index + 0] = y; // 0 ,1 ,... ,255
    
    ImgData[index + 1] = y;
    
    ImgData[index + 2] = y;
    
    fprintf(fpCsv, "%d, %d, %d \n ", index, index+1, index+2);
    
    }
    
    fprintf(fpCsv, "Count = %d\n\n", x);
    
    } 
    
    fclose(fpCsv);
    
     
    
    BitBlt(hDC, 0, 0, 512, 256, hDCMem, 0, 0, SRCCOPY);
    
    //SelectObject(hDCMem,oldbmp); 
    
    DeleteDC(hDCMem); 
    
    DeleteObject(bitmap); 
    
    //Invalidate(false);
    
    }
    I have one more doubt, Index are calculated using by pitch, x & y variables & also storing the data like

    Line 1 : 0,512,1024,... //Index value
    Line 2 : 1,513,1025....
    .
    .
    Line 512 : 2,514,2048....

    why will we store the data in this format?

    Pls find the attachment. Am I going in the right path?
    Attached Files Attached Files
    Last edited by saraswathisrinath; February 26th, 2013 at 03:35 AM. Reason: Forget to load the attachment
    Regards,

    SaraswathiSrinath

  10. #10
    Join Date
    Feb 2013
    Location
    United States
    Posts
    56

    Re: Draw Image Using 2D Byte Array ?

    First I would like to point out that there was an error in my code above that handles the WM_PAINT message. It should have read
    Code:
    	case WM_PAINT:
    		BeginPaint(WindowHandle, &Paint);
    		BitBlt(Paint.DeviceContextHandle,
    		       Paint.Rectangle.Left, Paint.Rectangle.Top,
    		       Paint.Rectangle.Right - Paint.Rectangle.Left,
    		       Paint.Rectangle.Bottom - Paint.Rectangle.Top,
    		       DeviceContextHandle, Paint.Rectangle.Left, Paint.Rectangle.Top, SRCCOPY);
    		EndPaint(WindowHandle, &Paint);
    where DeviceContextHandle is the device context handle returned by the call to CreateCompatibleDC(). I am using the BeginPaint() and EndPaint() functions so that the entire bitmap does not have to be copied for each paint event. Only the portion of the bitmap needed to redraw the client area is copied. Note that I am not using MFC, so the window handle has to be explicitly passed to the GetDC(), BeginPaint(), and EndPaint() Windows API functions. SaraswathiSrinath, you are passing the correct source device context handle in your call to BitBlt(), so maybe you caught my error.

    Now about your code ... You should be creating the bitmap object and compatible device context in the window's create event, not in the paint event. You only want to create the bitmap object once. The paint event happens every time the window's client area needs to be redrawn, such as when another window is moved over the top of it. The code to fill the bitmap should be elsewhere as well. In your example, it should probably be in the window's create event after the bitmap object is created. The bitmap handle, the compatible device context handle, and the pointer to the image data will need to be stored in static variables so that their values are retained between events. The compatible device context and the bitmap object should be deleted in the window's destroy event. The image data memory will automatically be released when the bitmap object is deleted. Also, writing files from the paint event is a bad idea since the paint event occurs many, many times.

    I have always used displays with 24-bit color. I do not understand why you are using 32 bits per pixel. 8-bit color requires a color palette, but that is probably not the route you want to take for modern graphic cards. Your code assigns the same value to the red, green, and blue components of the pixels, which is the right way to get gray.

    The image data is stored as a series of horizontal rows. Each row contains 3 bytes per pixel, plus up to 3 unused bytes in order to make the number of bytes in a row a multiple of 4. Your variable pitch would be the number of bytes it takes to store one row of the bitmap, though you are using 32 bits per pixel instead of 24. The first byte of each pixel is the blue component. The second byte is the green component. The third byte is the red component. If you were to look inside a bitmap image file, the image data would appear exactly as it does in the image data array.

  11. #11
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    Hi Mr.Dave,

    Yes. First I was create the bitmap object and compatible device context in the window's create event,

    But didn't worked, so that I wrote the code in paint event. May be My code contain anything wrong.

    This is first time I'm handling Bitmap with real-time data.

    My requirement is, I get the [1024][256] data at every sec.

    Using this data I make the bitmap in grayscale with fast display. But I'm sure , I'm getting 8bits per pixels.

    So that only I used the Bit-count = 8; Is it correct? For this requirement, can I use the 24-bit color displays?

    I changed my code like below for bit-count = 8 :

    Code:
    bi.bmiHeader.biBitCount = 8;
    int pitch = 1*512; 
    index += x*1; 
    ImgData[index + 0] = y;
    But I don't know is this correct?

    Thanks for your reply. I will correct my code and come back.

    For you experience, you have any sample code means, Pls post me for understand the concept.
    Regards,

    SaraswathiSrinath

  12. #12
    Join Date
    Feb 2013
    Location
    United States
    Posts
    56

    Resolved Re: Draw Image Using 2D Byte Array ?

    The bitmap object handle and the compatible device context handle need to be stored such that their values are retained after the window's create event is done. I have never used MFC, but apparently it splits the window's events into separate functions. This prohibits the use of local static variables to store information needed by multiple events. Perhaps, you could make the bitmap object handle and compatible device context handle into class variables. As a very last resort, you could make them global variables.

    I played around with creating a color palette for a bitmap that uses 8 bits per pixel. The following code listing is a complete test application. I am not using MFC.
    Code:
    #include <windows.h>
    
    LRESULT CALLBACK ProcessWindowEvent(HWND WindowHandle, UINT MessageCode, WPARAM WParameter, LPARAM LParameter)
    {
    	static int ClientWidth = 0;
    	static int ClientHeight = 0;
    	static int NumberOfBytesPerRow = 0;
    	static HBITMAP BitmapObjectHandle = NULL;
    	static BYTE* ImageData = NULL;
    	static HDC CompatibleDeviceContextHandle = NULL;
    
    	struct MyBITMAPINFO
    	{
    		BITMAPINFOHEADER bmiHeader;
    		RGBQUAD bmiColors[256];
    	};
    
    	LRESULT ReturnValue;
    	RECT ClientRectangle;
    	MyBITMAPINFO Bitmap;
    	int Index;
    	HDC WindowDeviceContextHandle;
    	int y;
    	int x;
    	PAINTSTRUCT Paint;
    
    	ReturnValue = 0;
    	switch (MessageCode)
    	{
    	case WM_CREATE:
    		GetClientRect(WindowHandle, &ClientRectangle);
    		ClientWidth = ClientRectangle.right - ClientRectangle.left;
    		ClientHeight = ClientRectangle.bottom - ClientRectangle.top;
    		NumberOfBytesPerRow = (ClientWidth + 3) & 0xFFFFFFFC;
    
    		Bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    		Bitmap.bmiHeader.biWidth = ClientWidth;
    		Bitmap.bmiHeader.biHeight = -ClientHeight;
    		Bitmap.bmiHeader.biPlanes = 1;
    		Bitmap.bmiHeader.biBitCount = 8;
    		Bitmap.bmiHeader.biCompression = BI_RGB;
    		Bitmap.bmiHeader.biSizeImage = NumberOfBytesPerRow * ClientHeight;
    		Bitmap.bmiHeader.biXPelsPerMeter = 0;
    		Bitmap.bmiHeader.biYPelsPerMeter = 0;
    		Bitmap.bmiHeader.biClrUsed = 256;
    		Bitmap.bmiHeader.biClrImportant = 256;
    		for (Index = 0; Index < 256; Index = Index + 1)
    		{
    			Bitmap.bmiColors[Index].rgbBlue = Index;
    			Bitmap.bmiColors[Index].rgbGreen = Index;
    			Bitmap.bmiColors[Index].rgbRed = Index;
    			Bitmap.bmiColors[Index].rgbReserved = 0;
    		}
    		WindowDeviceContextHandle = GetDC(WindowHandle);
    		BitmapObjectHandle = CreateDIBSection(WindowDeviceContextHandle, (BITMAPINFO*)(&Bitmap), DIB_RGB_COLORS, (void**)(&ImageData), NULL, 0);
    		if (BitmapObjectHandle != NULL)
    		{
    			CompatibleDeviceContextHandle = CreateCompatibleDC(WindowDeviceContextHandle);
    			if (CompatibleDeviceContextHandle != NULL)
    			{
    				SelectObject(CompatibleDeviceContextHandle, BitmapObjectHandle);
    				for (y = 0; y < ClientHeight; y = y + 1)
    				{
    					Index = y * NumberOfBytesPerRow;
    					for (x = 0; x < ClientWidth; x = x + 1)
    					{
    						ImageData[Index] = y;
    						Index = Index + 1;
    					}
    				}			
    			}
    		}
    		break;
    	case WM_DESTROY:
    		if (CompatibleDeviceContextHandle != NULL)
    		{
    			DeleteDC(CompatibleDeviceContextHandle);
    		}
    		if (BitmapObjectHandle != NULL)
    		{
    			DeleteObject(BitmapObjectHandle);
    		}
    		PostQuitMessage(0);
    		break;
    	case WM_ERASEBKGND:
    		break;
    	case WM_PAINT:
    		if (CompatibleDeviceContextHandle != NULL)
    		{
    			BeginPaint(WindowHandle, &Paint);
    			BitBlt(Paint.hdc, Paint.rcPaint.left, Paint.rcPaint.top,
    			       Paint.rcPaint.right - Paint.rcPaint.left, Paint.rcPaint.bottom - Paint.rcPaint.top,
    			       CompatibleDeviceContextHandle, Paint.rcPaint.left, Paint.rcPaint.top, SRCCOPY);
    			EndPaint(WindowHandle, &Paint);
    		}
    		break;
    	default:
    		ReturnValue = DefWindowProc(WindowHandle, MessageCode, WParameter, LParameter);
    	}
    
    	return ReturnValue;
    }
    
    int APIENTRY WinMain(HINSTANCE InstanceHandle, HINSTANCE PreviousInstanceHandle, char* CommandLine, int CommandShow)
    {
    	int ReturnValue;
    	WNDCLASS Window;
    	char* ClassName = "Test";
    	short int WindowClassAtom;
    	RECT WindowRectangle;
    	HWND WindowHandle;
    	int WindowMessageWasFound;
    	MSG WindowMessage;
    
    	ReturnValue = 0;
    	Window.style = CS_OWNDC;
    	Window.lpfnWndProc = &ProcessWindowEvent;
    	Window.cbClsExtra = 0;
    	Window.cbWndExtra = 0;
    	Window.hInstance = InstanceHandle;
    	Window.hIcon = NULL;
    	Window.hCursor= LoadCursor(NULL, IDC_ARROW);
    	Window.hbrBackground= HBRUSH(GetStockObject(NULL_BRUSH));
    	Window.lpszMenuName = NULL;
    	Window.lpszClassName = ClassName;
    	WindowClassAtom = RegisterClass(&Window);
    	if (WindowClassAtom != 0)
    	{
    		WindowRectangle.left = 0;
    		WindowRectangle.right = 512;
    		WindowRectangle.top = 0;
    		WindowRectangle.bottom = 256;
    		AdjustWindowRect(&WindowRectangle, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, false);
    		WindowHandle = CreateWindow(ClassName, "Test App",
    		                            WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
    		                            CW_USEDEFAULT, CW_USEDEFAULT, WindowRectangle.right - WindowRectangle.left,
    		                            WindowRectangle.bottom - WindowRectangle.top, NULL, NULL, InstanceHandle, NULL);
    		if (WindowHandle != NULL)
    		{
    			ShowWindow(WindowHandle, CommandShow);
    			UpdateWindow(WindowHandle);
    
    			WindowMessageWasFound = GetMessage(&WindowMessage, NULL, 0, 0);
    			while (WindowMessageWasFound)
    			{
    				TranslateMessage(&WindowMessage);
    				DispatchMessage(&WindowMessage);
    				ReturnValue = WindowMessage.wParam;
    				WindowMessageWasFound = GetMessage(&WindowMessage, NULL, 0, 0);
    			}
    		}
    		UnregisterClass(ClassName, InstanceHandle);
    	}
    
    	return ReturnValue;
    }

  13. #13
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    Thank you for your reply.

    But I know only MFC

    As you said I create the CreateCompatibleDC(hDC) in the Constructor instead of OnPaint() Method.

    But if any other window appear under the application, Its not working.

    I will read your code & Try my level best.
    Regards,

    SaraswathiSrinath

  14. #14
    Join Date
    Nov 2011
    Location
    India
    Posts
    333

    Re: Draw Image Using 2D Byte Array ?

    Hi Mr.Dave,

    I Done the concept using your code. Really Very Happy.

    Please find the attachment & run it.

    I have one more doubt.

    In that code I will replace the real time data like below in OnPaint() method, Am I going in the correct path?
    Code:
    for (y = 0; y < ClientHeight; y = y + 1)
    	{
    		Index = y * NumberOfBytesPerRow;
    		for (x = 0; x < ClientWidth; x = x + 1)
    		{
    			ImageData[Index] = RealtimeData[y];
    			Index = Index + 1;
    		}
    	}
    I like to paint the Real time data continuously. How Can I run the OnPaint method continuously?

    I'm using Dialog Based Application.

    For continuous real time data display, I used Invalidate(false); [OR] UpdateData(true); method means,Buttons are hidden at run time and also hidden if any other window maximize and minimize case.

    Waiting for your valuable reply
    Attached Files Attached Files
    Regards,

    SaraswathiSrinath

  15. #15
    Join Date
    Feb 2013
    Location
    United States
    Posts
    56

    Smile Re: Draw Image Using 2D Byte Array ?

    Since Windows is event driven, there needs to be an event that occurs periodically in order for the display to be updated with new data. This event could be a timer event. You start a timer by calling SetTimer(), perhaps in the create event code, and stop the timer by calling KillTimer(), perhaps in the destroy event code. It is within the code that handles the timer event that you want to sample the incoming data and redraw the image accordingly. After the image has been redrawn, call InvalidateRect(). This will put a paint message on the window's event queue. The code in the paint event then calls BitBlt() to update the display. To repaint the display immediately, call UpdateWindow() after the call to InvalidateRect(). Make sure to use BeginPaint() and EndPaint() in the code for the paint event. Otherwise, the paint event will be invoked endlessly.

    Code:
    	case WM_PAINT:
    		// The image should not be redrawn in the paint event.
    		// The display just gets updated with the current image.
    		BeginPaint(WindowHandle, &Paint);
    		BitBlt(Paint.hdc, Paint.rcPaint.left, Paint.rcPaint.top,
    		       Paint.rcPaint.right - Paint.rcPaint.left, Paint.rcPaint.bottom - Paint.rcPaint.top,
    		       CompatibleDeviceContextHandle, Paint.rcPaint.left, Paint.rcPaint.top, SRCCOPY);
    		EndPaint(WindowHandle, &Paint);
    		break;
    	case WM_TIMER:
    
    		// < code to sample the incoming data and redraw the image >
    
    		// Update the entire client area.
    		InvalidateRect(WindowHandle, NULL, false);
    		UpdateWindow(WindowHandle);
    		break;

Page 1 of 3 123 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured