⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pddvclas.cpp

📁 在WINCE下使用USB摄像头的万能驱动源码
💻 CPP
📖 第 1 页 / 共 5 页
字号:
//-------------------------------------------------------------------------
// <copyright file="pddVClas.cpp" company="Microsoft">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
//
//    The use and distribution terms for this software are covered by the
//    Microsoft Limited Permissive License (Ms-LPL) 
//    http://www.microsoft.com/resources/sharedsource/licensingbasics/limitedpermissivelicense.mspx 
//    which can be found in the file MS-LPL.txt at the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by
//    the terms of this license.
//
//    You must not remove this notice, or any other, from this software.
// </copyright>
// 
// <summary>
//    Low level USB video interface code
// </summary>
//-------------------------------------------------------------------------
//======================================================================
// pddVClas.cpp - Low level USB video interface code
//
// Author: Douglas Boling
//======================================================================
#include <windows.h>                // For all that Windows stuff
#include <USBdi.h>					// USB includes
#include <usb100.h>					// USB includes
#include <usbclient.h>				// USB client driver helper code
#include "usbvideo.h"				// USB Video Specification defs

#include "webcamsdk.h"				// IOCTL defs for driver
#include "WebCam.h" 				// Local driver includes
#include "pddVClas.h"				// PDD defs


//
// Controllable features of the Camera.  The order of this table must be the same as the bitfields of the 
// Camera input descriptor feature bit fields.
//
/*
FEATURESTRUCT CamFeatures[] = 
{
//    Feature ID                  USB Video Class Command ID                     Unit ID            Interface  Length
	{FEAT_SCANNING_MODE,      USB_VIDEO_CT_CS_SCANNING_MODE_CTL,           USB_VIDEO_VC_INPUT_TERMINAL, 0,  1},
	{FEAT_AUTO_EXPOSURE_MODE, USB_VIDEO_CT_CS_AE_MODE_CTL               ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  1},
	{FEAT_AUTO_EXPOSURE_PRI,  USB_VIDEO_CT_CS_AE_PRIORITY_CTL           ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  1},
	{FEAT_EXPOSURE_TIME_ABS,  USB_VIDEO_CT_CS_EXPOSURE_TIME_ABSOLUTE_CTL,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  4},
	{FEAT_EXPOSURE_TIME_REL,  USB_VIDEO_CT_CS_EXPOSURE_TIME_RELATIVE_CTL,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  1},
	{FEAT_FOCUS_ABS,          USB_VIDEO_CT_CS_FOCUS_ABSOLUTE_CTL        ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  2},
	{FEAT_FOCUS_REL,          USB_VIDEO_CT_CS_FOCUS_RELATIVE_CTL        ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  2},
	{FEAT_IRIS_ABS,           USB_VIDEO_CT_CS_IRIS_ABSOLUTE_CTL         ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  1},
	{FEAT_IRIS_REL,           USB_VIDEO_CT_CS_IRIS_RELATIVE_CTL         ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  2},
	{FEAT_ZOOM_ABS,           USB_VIDEO_CT_CS_ZOOM_ABSOLUTE_CTL         ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  1},
	{FEAT_ZOOM_REL,           USB_VIDEO_CT_CS_ZOOM_RELATIVE_CTL         ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  2},
	{FEAT_PANTILT_ABS,        USB_VIDEO_CT_CS_PANTILT_ABSOLUTE_CTL      ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  3},
	{FEAT_PANTILT_REL,        USB_VIDEO_CT_CS_PANTILT_RELATIVE_CTL      ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  8},
	{FEAT_ROLL_ABS,           USB_VIDEO_CT_CS_ROLL_ABSOLUTE_CTL         ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  4},
	{FEAT_ROLL_REL,           USB_VIDEO_CT_CS_ROLL_RELATIVE_CTL         ,  USB_VIDEO_VC_INPUT_TERMINAL, 0,  2},
	{FEAT_UNSUPPORTED, 0, 0, 0, 0},  //Reserved 
	{FEAT_UNSUPPORTED, 0, 0, 0, 0},  //Reserved
	{FEAT_FOCUS_AUTO ,        USB_VIDEO_CT_CS_FOCUS_AUTO_CTL            ,  USB_VIDEO_VC_INPUT_TERMINAL, 0, 2},
	{FEAT_PRIVACY ,           USB_VIDEO_CT_CS_PRIVACY_CTL               ,  USB_VIDEO_VC_INPUT_TERMINAL, 0, 1},
};*/
FEATURESTRUCT CamFeatures[] = 
{
//    Feature ID                  USB Video Class Command ID                     Unit ID            Interface  Length
	{FEAT_SCANNING_MODE,      USB_VIDEO_CT_CS_SCANNING_MODE_CTL,           FEAT_UNSUPPORTED, 0,  1},
	{FEAT_AUTO_EXPOSURE_MODE, USB_VIDEO_CT_CS_AE_MODE_CTL               ,  FEAT_UNSUPPORTED, 0,  1},
	{FEAT_AUTO_EXPOSURE_PRI,  USB_VIDEO_CT_CS_AE_PRIORITY_CTL           ,  FEAT_UNSUPPORTED, 0,  1},
	{FEAT_EXPOSURE_TIME_ABS,  USB_VIDEO_CT_CS_EXPOSURE_TIME_ABSOLUTE_CTL,  FEAT_UNSUPPORTED, 0,  4},
	{FEAT_EXPOSURE_TIME_REL,  USB_VIDEO_CT_CS_EXPOSURE_TIME_RELATIVE_CTL,  FEAT_UNSUPPORTED, 0,  1},
	{FEAT_FOCUS_ABS,          USB_VIDEO_CT_CS_FOCUS_ABSOLUTE_CTL        ,  FEAT_UNSUPPORTED, 0,  2},
	{FEAT_FOCUS_REL,          USB_VIDEO_CT_CS_FOCUS_RELATIVE_CTL        ,  FEAT_UNSUPPORTED, 0,  2},
	{FEAT_IRIS_ABS,           USB_VIDEO_CT_CS_IRIS_ABSOLUTE_CTL         ,  FEAT_UNSUPPORTED, 0,  1},
	{FEAT_IRIS_REL,           USB_VIDEO_CT_CS_IRIS_RELATIVE_CTL         ,  FEAT_UNSUPPORTED, 0,  2},
	{FEAT_ZOOM_ABS,           USB_VIDEO_CT_CS_ZOOM_ABSOLUTE_CTL         ,  FEAT_UNSUPPORTED, 0,  1},
	{FEAT_ZOOM_REL,           USB_VIDEO_CT_CS_ZOOM_RELATIVE_CTL         ,  FEAT_UNSUPPORTED, 0,  2},
	{FEAT_PANTILT_ABS,        USB_VIDEO_CT_CS_PANTILT_ABSOLUTE_CTL      ,  FEAT_UNSUPPORTED, 0,  3},
	{FEAT_PANTILT_REL,        USB_VIDEO_CT_CS_PANTILT_RELATIVE_CTL      ,  FEAT_UNSUPPORTED, 0,  8},
	{FEAT_ROLL_ABS,           USB_VIDEO_CT_CS_ROLL_ABSOLUTE_CTL         ,  FEAT_UNSUPPORTED, 0,  4},
	{FEAT_ROLL_REL,           USB_VIDEO_CT_CS_ROLL_RELATIVE_CTL         ,  FEAT_UNSUPPORTED, 0,  2},
	{FEAT_UNSUPPORTED, 0, FEAT_UNSUPPORTED, 0, 0},  //Reserved 
	{FEAT_UNSUPPORTED, 0, FEAT_UNSUPPORTED, 0, 0},  //Reserved
	{FEAT_FOCUS_AUTO ,        USB_VIDEO_CT_CS_FOCUS_AUTO_CTL            ,  FEAT_UNSUPPORTED, 0, 2},
	{FEAT_PRIVACY ,           USB_VIDEO_CT_CS_PRIVACY_CTL               ,  FEAT_UNSUPPORTED, 0, 1},
};

//
// Controllable features of the processor unit
//
/*
FEATURESTRUCT ProcFeatures[] = 
{
	{FEAT_BRIGHTNESS,               USB_VIDEO_PU_CS_BRIGHTNESS_CTL,                USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_CONTRAST,                 USB_VIDEO_PU_CS_CONTRAST_CTL,                  USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_HUE,                      USB_VIDEO_PU_CS_HUE_CTL,                       USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_SATURATION,               USB_VIDEO_PU_CS_SATURATION_CTL,                USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_SHARPNESS,                USB_VIDEO_PU_CS_SHARPNESS_CTL,                 USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_GAMMA,                    USB_VIDEO_PU_CS_GAMMA_CTL,                     USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_WHITE_BAL_TEMP,           USB_VIDEO_PU_CS_WHITE_BALANCE_TEMP_CTL,        USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_WHITE_BAL_COMPONENT,      USB_VIDEO_PU_CS_WHITE_BALANCE_COMPONENT_CTL,   USB_VIDEO_VC_PROCESSING_UNIT, 0,  4},
	{FEAT_BACKLIGHT_COMPENSATION,   USB_VIDEO_PU_CS_BACKLIGHT_COMPENSATION_CTL,    USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_GAIN,                     USB_VIDEO_PU_CS_GAIN_CTL,                      USB_VIDEO_VC_PROCESSING_UNIT, 0,  2},
	{FEAT_POWER_LINE_FREQ,          USB_VIDEO_PU_CS_POWER_LINE_FREQUENCY_CTL,      USB_VIDEO_VC_PROCESSING_UNIT, 0,  1},
	{FEAT_AUTO_HUE,                 USB_VIDEO_PU_CS_HUE_AUTO_CTL,                  USB_VIDEO_VC_PROCESSING_UNIT, 0,  1},
	{FEAT_AUTO_WHITE_BAL_TEMP,      USB_VIDEO_PU_CS_WHITE_BALANCE_TEMP_AUTO_CTL,   USB_VIDEO_VC_PROCESSING_UNIT, 0,  1},
	{FEAT_AUTO_WHITE_BAL_COMPONENT, USB_VIDEO_PU_CS_WHITE_BALANCE_COMPONENT_AUTO_CTL,  USB_VIDEO_VC_PROCESSING_UNIT, 0, 1},
	{FEAT_DIGITAL_MULTIPLIER,       USB_VIDEO_PU_CS_DIGITAL_MULTIPLIER_CTL,        USB_VIDEO_VC_PROCESSING_UNIT, 0, 2},
	{FEAT_DIGITAL_MULTIPLIER_LIMIT, USB_VIDEO_PU_CS_DIGITAL_MULTIPLIER_LIMIT_CTL,  USB_VIDEO_VC_PROCESSING_UNIT, 0, 2},
	{FEAT_ANALOG_VIDEO_STANDARD,    USB_VIDEO_PU_CS_ANALOG_VIDEO_STANDARD_CTL,     USB_VIDEO_VC_PROCESSING_UNIT, 0, 1},
	{FEAT_ANALOG_VIDEO_LOCK_STATUS, USB_VIDEO_PU_CS_ANALOG_LOCK_STATUS_CTL,        USB_VIDEO_VC_PROCESSING_UNIT, 0, 1},
};
*/
FEATURESTRUCT ProcFeatures[] = 
{
	{FEAT_BRIGHTNESS,               USB_VIDEO_PU_CS_BRIGHTNESS_CTL,                FEAT_UNSUPPORTED, 0,  2},
	{FEAT_CONTRAST,                 USB_VIDEO_PU_CS_CONTRAST_CTL,                  FEAT_UNSUPPORTED, 0,  2},
	{FEAT_HUE,                      USB_VIDEO_PU_CS_HUE_CTL,                       FEAT_UNSUPPORTED, 0,  2},
	{FEAT_SATURATION,               USB_VIDEO_PU_CS_SATURATION_CTL,                FEAT_UNSUPPORTED, 0,  2},
	{FEAT_SHARPNESS,                USB_VIDEO_PU_CS_SHARPNESS_CTL,                 FEAT_UNSUPPORTED, 0,  2},
	{FEAT_GAMMA,                    USB_VIDEO_PU_CS_GAMMA_CTL,                     FEAT_UNSUPPORTED, 0,  2},
	{FEAT_WHITE_BAL_TEMP,           USB_VIDEO_PU_CS_WHITE_BALANCE_TEMP_CTL,        FEAT_UNSUPPORTED, 0,  2},
	{FEAT_WHITE_BAL_COMPONENT,      USB_VIDEO_PU_CS_WHITE_BALANCE_COMPONENT_CTL,   FEAT_UNSUPPORTED, 0,  4},
	{FEAT_BACKLIGHT_COMPENSATION,   USB_VIDEO_PU_CS_BACKLIGHT_COMPENSATION_CTL,    FEAT_UNSUPPORTED, 0,  2},
	{FEAT_GAIN,                     USB_VIDEO_PU_CS_GAIN_CTL,                      FEAT_UNSUPPORTED, 0,  2},
	{FEAT_POWER_LINE_FREQ,          USB_VIDEO_PU_CS_POWER_LINE_FREQUENCY_CTL,      FEAT_UNSUPPORTED, 0,  1},
	{FEAT_AUTO_HUE,                 USB_VIDEO_PU_CS_HUE_AUTO_CTL,                  FEAT_UNSUPPORTED, 0,  1},
	{FEAT_AUTO_WHITE_BAL_TEMP,      USB_VIDEO_PU_CS_WHITE_BALANCE_TEMP_AUTO_CTL,   FEAT_UNSUPPORTED, 0,  1},
	{FEAT_AUTO_WHITE_BAL_COMPONENT, USB_VIDEO_PU_CS_WHITE_BALANCE_COMPONENT_AUTO_CTL,  FEAT_UNSUPPORTED, 0, 1},
	{FEAT_DIGITAL_MULTIPLIER,       USB_VIDEO_PU_CS_DIGITAL_MULTIPLIER_CTL,        FEAT_UNSUPPORTED, 0, 2},
	{FEAT_DIGITAL_MULTIPLIER_LIMIT, USB_VIDEO_PU_CS_DIGITAL_MULTIPLIER_LIMIT_CTL,  FEAT_UNSUPPORTED, 0, 2},
	{FEAT_ANALOG_VIDEO_STANDARD,    USB_VIDEO_PU_CS_ANALOG_VIDEO_STANDARD_CTL,     FEAT_UNSUPPORTED, 0, 1},
	{FEAT_ANALOG_VIDEO_LOCK_STATUS, USB_VIDEO_PU_CS_ANALOG_LOCK_STATUS_CTL,        FEAT_UNSUPPORTED, 0, 1},
};

//-----------------------------------------------------------------------
// Pdd_DeviceAttach - Called by MDD during USBDeviceAttach.  
// Return:
//     0 - Not my interface, ignore
//     1 - Claim interface
//     2 - Pass along interface to LoadGenericInterfaceDriver
// 
int pdd_DeviceAttach (USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
                  LPCUSB_INTERFACE lpInterface, LPCWSTR szUniqueDriverId,
                  LPCUSB_DRIVER_SETTINGS lpDriverSettings, DWORD *pdwContext)
{
	int rc = 0;

	// Call the device to query all interfaces
   	LPCUSB_DEVICE lpUsbDev = (lpUsbFuncs->lpGetDeviceInfo)(hDevice);
	if (!lpUsbDev)
		return 0;

	// See if this device has the interface we need...
	// Only accept video control interface
	if ((DWORD)lpInterface != 0)
	{
		if (lpInterface->Descriptor.bInterfaceSubClass == 0x02)
		{
			return 2;
		}
	}

	// Allocate the Phys dev driver context structure.  We'll add this
	// pointer to the pdd field available in the device driver context
	// structure.
	PPDDCONTEXT pPDD = (PPDDCONTEXT)LocalAlloc (LPTR, sizeof (PDDCONTEXT)); 
	if (pPDD == 0)
	{
		SetLastError (ERROR_NOT_ENOUGH_MEMORY);
		return 0;
	}
	pPDD->dwSize = sizeof (PDDCONTEXT);
	InitializeCriticalSection (&pPDD->csStill);
	pPDD->hStillEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
	pPDD->wReadThreadState = STREAMTHD_STOPPED;
	pPDD->wCurrFormatIndex = 0xffff;
	pPDD->wCurrFrameIndex = 0xffff;
	pPDD->hVendorEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
	pPDD->hNewFrameEvent = CreateEvent (NULL, TRUE, FALSE, NULL); // Manual reset event

	// Set up default stream receive buffer
	pPDD->strStreamDefault.dwNumBuffs = NUMDEFBUFFS;
	InitializeCriticalSection (&pPDD->strStreamDefault.csBuffSync);
	pPDD->strStreamDefault.hNewFrame = NULL;
	pPDD->strStreamDefault.dwBuffSize = 0;
	pPDD->strStreamDefault.pFrame[0].pBuff = 0;
	pPDD->strStreamDefault.pFrame[1].pBuff = 0;

	// See if we can find the proper interfaces.  First, look for the
	// standard Video Class Device interfaces...
	if (!ParseStreamInterfaces (pPDD, lpUsbDev, 0x0e, 1, 0x0e, 2))
	{
		// If failed to open std interface, try vendor specific
		if (!ParseStreamInterfaces (pPDD, lpUsbDev, 0xff, 1, 0xff, 2))
		{
			FreePDDContext (pPDD);
			return 0;
		} 
	}
	DEBUGMSG(ZONE_USBLOAD, (DTAG TEXT("Video Class interface found.\n"), rc)); 

	// Parse to see what features are supported by the camera
	if (ParseFeatureParameters (pPDD))
		DEBUGMSG(ZONE_USBLOAD, (DTAG TEXT("Error calling ParseFeatureParameters\n"))); 

	rc = 1;  // We accept the device

	// Save our hardware context in the driver context structure.
	*pdwContext = (DWORD)pPDD;
	return rc;
}
//---------------------------------------------------------------------------------------
// FreePDDContext - Free the PDD context structure.
//
void FreePDDContext (PPDDCONTEXT pPDD)
{
	int i;
	if (pPDD == 0)
		return;
	__try 
	{
		if (pPDD->dwSize == sizeof (PDDCONTEXT))
		{
			// Stop the read thread if its running.
			StopReadThread (pPDD);

			DeleteCriticalSection (&pPDD->csStill);

			if (pPDD->hStillEvent)
				CloseHandle (pPDD->hStillEvent);

			// Free the stream descriptors
			if (pPDD->usbstrmIF) 
			{
				for (i = 0; i < pPDD->nStreamInterfaces; i++)
					if (pPDD->usbstrmIF[i].lpepExtDesc)
						LocalFree (pPDD->usbstrmIF[i].lpepExtDesc);
				LocalFree (pPDD->usbstrmIF);
			}

			// Free any control interface extended descriptor
			if (pPDD->usbctlIF.lpepExtDesc)
				LocalFree (pPDD->usbctlIF.lpepExtDesc);

			if (pPDD->hReadThread)
				CloseHandle (pPDD->hReadThread);

			if (pPDD->strStreamDefault.dwReserved)
				CloseHandle ((HANDLE)pPDD->strStreamDefault.dwReserved);

			for (DWORD i = 0; i < pPDD->strStreamDefault.dwNumBuffs; i++)
				if (pPDD->strStreamDefault.pFrame[i].pBuff)
					LocalFree (pPDD->strStreamDefault.pFrame[i].pBuff);

			DeleteCriticalSection (&pPDD->strStreamDefault.csBuffSync);

			if (pPDD->hVendorEvent)
				CloseHandle (pPDD->hVendorEvent);

			if (pPDD->hNewFrameEvent)
				CloseHandle (pPDD->hNewFrameEvent);
		}
		LocalFree (pPDD);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		DEBUGMSG(ZONE_ERROR, (DTAG TEXT("Exception freeing PDD context structure\n"))); 
	}
	return;
}
//-----------------------------------------------------------------------
// Pdd_DeviceDetach - Called by MDD during unload notification but
// before the stream driver is deactivated.
// 
int pdd_DeviceDetach (PDRVCONTEXT pDrv)
{
	// Set the stream interface to turn off the video stream.
	int rc = SetStreamInterface (pDrv, 1, 0);

	// Free up PDD context structure
	FreePDDContext ((PPDDCONTEXT)pDrv->dwPddContext);
	return 0;
}

//---------------------------------------------------------------------------------------
//
//
int pdd_DrvInit (PDRVCONTEXT pDrv) 
{
	return 1;
}

//---------------------------------------------------------------------------------------
//
//
void pdd_DrvUninit (PDRVCONTEXT pDrv) 
{
	return;
}

//---------------------------------------------------------------------------------------
// pdd_DrvOpen - Called when driver is opened
//
int pdd_DrvOpen (PDRVCONTEXT pDrv) 
{
	BOOL fPwr;

	// Make sure the camera is powered
	GetPower (pDrv, &fPwr);

	if (!fPwr)
		SetPower (pDrv, TRUE);
	return 1;
}

//---------------------------------------------------------------------------------------
// pdd_DrvClose - Called when driver is closed
//
void pdd_DrvClose (PDRVCONTEXT pDrv) 
{
	int rc;

	rc = pdd_StopVidStream (pDrv);

	// Turn off the camera
	SetPower (pDrv, FALSE);
	return;
}

//---------------------------------------------------------------------------------------
// Return a list of Feature IDs that is supported by this camera
//
int pdd_GetFeatureList (PDRVCONTEXT pDrv, DWORD *pList, int nListSize)
{
	int nCnt = 0; 
	int i;

	// Count the number of supported features
	for (i = 0; i < dim (CamFeatures); i++)
	{
//		if (CamFeatures[i].bFeatureID != FEAT_UNSUPPORTED)
		if (CamFeatures[i].bUnit != FEAT_UNSUPPORTED)
			nCnt++;
	}
	for (i = 0; i < dim (ProcFeatures); i++)
	{
//		if (ProcFeatures[i].bFeatureID != FEAT_UNSUPPORTED)
		if (ProcFeatures[i].bUnit != FEAT_UNSUPPORTED)
			nCnt++;
	}

	// See if they're just asking for the number of features
	if (pList == 0)
		return nCnt;

	if (nListSize < nCnt)
		return 0;

	// Now compile the list of supported IDs
	for (i = 0; i < dim (CamFeatures); i++)
	{
//		if (CamFeatures[i].bFeatureID != FEAT_UNSUPPORTED)
		if (CamFeatures[i].bUnit != FEAT_UNSUPPORTED)
			*pList++ = CamFeatures[i].bFeatureID;
	}
	for (i = 0; i < dim (ProcFeatures); i++)
	{
//		if (ProcFeatures[i].bFeatureID != FEAT_UNSUPPORTED)
		if (ProcFeatures[i].bUnit != FEAT_UNSUPPORTED)
			*pList++ = ProcFeatures[i].bFeatureID;
	}
	return nCnt;
}
//-----------------------------------------------------------------------
// pdd_QueryFeature - Gets the min and max values value of a feature 
// 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -