2011-11-13 6 views
27

मेरे पास एक जीयूआई एप्लीकेशन है जिसमें टूलबार बटन, मेनू ग्लाइफ, अधिसूचना आइकन आदि के लिए उपयोग किए जाने वाले कई आइकन शामिल हैं। ये आइकन संसाधन के रूप में एप्लिकेशन से जुड़े हुए हैं और विभिन्न आकारों के विभिन्न प्रकार उपलब्ध हैं। आमतौर पर, टूलबार बटन छवियों के लिए मेरे पास 16px, 24px और 32px संस्करण उपलब्ध हैं। आंशिक पारदर्शिता के साथ मेरे आइकन 32 बीपीपी हैं।एलियासिंग से पीड़ित किए बिना संसाधन से आइकन कैसे लोड करूं?

एप्लिकेशन उच्च डीपीआई जागरूक है और मौजूदा फ़ॉन्ट स्केलिंग के अनुसार सभी दृश्य तत्वों के आकार को समायोजित करता है। तो, उदाहरण के लिए, 100% फ़ॉन्ट स्केलिंग पर, 96 डीपीआई, टूलबार आइकन आकार 16px है। 125% स्केलिंग पर, 120 डीपीआई, टूलबार आइकन आकार 20px है। मुझे बिना किसी एलियासिंग प्रभाव के आकार 20px के आइकन को लोड करने में सक्षम होना चाहिए। मैं यह कैसे कर सकता हूँ? ध्यान दें कि मैं विंडोज 2000 और बाद में समर्थन करना चाहता हूं।

+6

यदि किसी ने इसे याद किया है, तो यह प्रश्न + उत्तर अनुरोध द्वारा किया गया था। –

उत्तर

27

Vista पर और कई नए कार्यों को जोड़ा गया जो इस कार्य को तुच्छ बनाते हैं। यह समारोह यहां सबसे उपयुक्त है LoadIconWithScaleDown है।

यह फ़ंक्शन पहले आइकन के लिए आइकन फ़ाइल को बिल्कुल उसी आकार के लिए खोजेगा। यदि कोई मिलान नहीं मिलता है, तब तक जब तक दोनों सीएक्स और साइ मानक आइकन आकार -16, 32, 48, या 256 पिक्सेल से मेल नहीं खाते हैं- अगला सबसे बड़ा आइकन चुना जाता है और फिर वांछित आकार में स्केल किया जाता है। उदाहरण के लिए, यदि कॉलिग एप्लिकेशन द्वारा 40 पिक्सेल के एक्स आयाम वाले आइकन का अनुरोध किया गया है, तो 48-पिक्सेल आइकन का उपयोग किया जाता है और 40 पिक्सेल तक स्केल किया जाता है। इसके विपरीत, लोडइमेज फ़ंक्शन 32-पिक्सेल आइकन का चयन करता है और इसे 40 पिक्सेल तक स्केल करता है।

यदि फ़ंक्शन एक बड़े आइकन का पता लगाने में असमर्थ है, तो यह अगले छोटे आइकन को ढूंढने और वांछित आकार तक स्केल करने के मानक व्यवहार के लिए डिफ़ॉल्ट रूप से डिफ़ॉल्ट होता है।

मेरे अनुभव में यह कार्य स्केलिंग का उत्कृष्ट काम करता है और परिणाम एलियासिंग का कोई संकेत नहीं दिखाते हैं।

विंडोज के पुराने संस्करणों के लिए, मेरे ज्ञान के सबसे अच्छे से, कोई भी कार्य नहीं है जो इस कार्य को पर्याप्त रूप से कर सकता है। LoadImage से प्राप्त परिणाम बहुत खराब गुणवत्ता वाले हैं।

  1. संसाधन में उपलब्ध छवियों की जांच सबसे बड़े आकार कि वांछित आइकन आकार की तुलना में कम है के साथ छवि को खोजने के लिए: इसके बजाय सबसे अच्छा तरीका मैं पाया है इस प्रकार है।
  2. वांछित आकार का एक नया आइकन बनाएं और इसे पूरी तरह पारदर्शी होने के लिए प्रारंभ करें।
  3. नए (बड़े) आइकन के केंद्र में संसाधन से छोटे आइकन को रखें।

इसका मतलब है कि आइकन के चारों ओर एक छोटी पारदर्शी सीमा होगी, लेकिन आम तौर पर यह महत्वहीन होने के लिए काफी छोटा है। आदर्श विकल्प कोड का उपयोग करना होगा जो LoadIconWithScaleDown के अनुसार स्केल कर सकता है, लेकिन यह लिखना गैर-तुच्छ है।

तो, बिना किसी विज्ञापन के कोड मैं उपयोग करता हूं।

unit uLoadIconResource; 

interface 

uses 
    SysUtils, Math, Classes, Windows, Graphics, CommCtrl; 

function LoadIconResourceSize(const ResourceName: string; IconSize: Integer): HICON;//will not throw an exception 
function LoadIconResourceMetric(const ResourceName: string; IconMetric: Integer): HICON; 

implementation 

function IconSizeFromMetric(IconMetric: Integer): Integer; 
begin 
    case IconMetric of 
    ICON_SMALL: 
    Result := GetSystemMetrics(SM_CXSMICON); 
    ICON_BIG: 
    Result := GetSystemMetrics(SM_CXICON); 
    else 
    raise EAssertionFailed.Create('Invalid IconMetric'); 
    end; 
end; 

procedure GetDIBheaderAndBits(bmp: HBITMAP; out bih: BITMAPINFOHEADER; out bits: Pointer); 
var 
    pbih: ^BITMAPINFOHEADER; 
    bihSize, bitsSize: DWORD; 
begin 
    bits := nil; 
    GetDIBSizes(bmp, bihSize, bitsSize); 
    pbih := AllocMem(bihSize); 
    Try 
    bits := AllocMem(bitsSize); 
    GetDIB(bmp, 0, pbih^, bits^); 
    if pbih.biSize<SizeOf(bih) then begin 
     FreeMem(bits); 
     bits := nil; 
     exit; 
    end; 
    bih := pbih^; 
    Finally 
    FreeMem(pbih); 
    End; 
end; 

function CreateIconFromSmallerIcon(IconSize: Integer; SmallerIcon: HICON): HICON; 

    procedure InitialiseBitmapInfoHeader(var bih: BITMAPINFOHEADER); 
    begin 
    bih.biSize := SizeOf(BITMAPINFOHEADER); 
    bih.biWidth := IconSize; 
    bih.biHeight := 2*IconSize;//height of xor bitmap plus height of and bitmap 
    bih.biPlanes := 1; 
    bih.biBitCount := 32; 
    bih.biCompression := BI_RGB; 
    end; 

    procedure CreateXORbitmap(const sbih, dbih: BITMAPINFOHEADER; sptr, dptr: PDWORD); 
    var 
    line, xOffset, yOffset: Integer; 
    begin 
    xOffset := (IconSize-sbih.biWidth) div 2; 
    yOffset := (IconSize-sbih.biHeight) div 2; 
    inc(dptr, xOffset + IconSize*yOffset); 
    for line := 0 to sbih.biHeight-1 do begin 
     Move(sptr^, dptr^, sbih.biWidth*SizeOf(DWORD)); 
     inc(dptr, IconSize);//relies on the fact that no padding is needed for RGBA scanlines 
     inc(sptr, sbih.biWidth);//likewise 
    end; 
    end; 

var 
    SmallerIconInfo: TIconInfo; 
    sBits, xorBits: PDWORD; 
    xorScanSize, andScanSize: Integer; 
    xorBitsSize, andBitsSize: Integer; 
    sbih: BITMAPINFOHEADER; 
    dbih: ^BITMAPINFOHEADER; 
    resbitsSize: DWORD; 
    resbits: Pointer; 

begin 
    Result := 0; 
    Try 
    if not GetIconInfo(SmallerIcon, SmallerIconInfo) then begin 
     exit; 
    end; 
    Try 
     GetDIBheaderAndBits(SmallerIconInfo.hbmColor, sbih, Pointer(sBits)); 
     if Assigned(sBits) then begin 
     Try 
      if (sbih.biWidth>IconSize) or (sbih.biHeight>IconSize) or (sbih.biPlanes<>1) or (sbih.biBitCount<>32) then begin 
      exit; 
      end; 

      xorScanSize := BytesPerScanline(IconSize, 32, 32); 
      Assert(xorScanSize=SizeOf(DWORD)*IconSize); 
      andScanSize := BytesPerScanline(IconSize, 1, 32); 
      xorBitsSize := IconSize*xorScanSize; 
      andBitsSize := IconSize*andScanSize; 
      resbitsSize := SizeOf(BITMAPINFOHEADER) + xorBitsSize + andBitsSize; 
      resbits := AllocMem(resbitsSize);//AllocMem zeroises the memory 
      Try 
      dbih := resbits; 
      InitialiseBitmapInfoHeader(dbih^); 

      xorBits := resbits; 
      inc(PByte(xorBits), SizeOf(BITMAPINFOHEADER)); 
      CreateXORbitmap(sbih, dbih^, sBits, xorBits); 

      //don't need to fill in the mask bitmap when using RGBA 
      Result := CreateIconFromResourceEx(resbits, resbitsSize, True, $00030000, IconSize, IconSize, LR_DEFAULTCOLOR); 
      Finally 
      FreeMem(resbits); 
      End; 
     Finally 
      FreeMem(sBits); 
     End; 
     end; 
    Finally 
     if SmallerIconInfo.hbmMask<>0 then begin 
     DeleteObject(SmallerIconInfo.hbmMask); 
     end; 
     if SmallerIconInfo.hbmColor<>0 then begin 
     DeleteObject(SmallerIconInfo.hbmColor); 
     end; 
    End; 
    Finally 
    DestroyIcon(SmallerIcon); 
    End; 
end; 

function LoadIconResourceSize(const ResourceName: string; IconSize: Integer): HICON;//will not throw an exception 

    function LoadImage(IconSize: Integer): HICON; 
    begin 
    Result := Windows.LoadImage(HInstance, PChar(ResourceName), IMAGE_ICON, IconSize, IconSize, LR_DEFAULTCOLOR); 
    end; 

type 
    TGrpIconDir = packed record 
    idReserved: Word; 
    idType: Word; 
    idCount: Word; 
    end; 

    TGrpIconDirEntry = packed record 
    bWidth: Byte; 
    bHeight: Byte; 
    bColorCount: Byte; 
    bReserved: Byte; 
    wPlanes: Word; 
    wBitCount: Word; 
    dwBytesInRes: DWORD; 
    wID: WORD; 
    end; 

var 
    i, BestAvailableIconSize, ThisSize: Integer; 
    ResourceNameWide: WideString; 
    Stream: TResourceStream; 
    IconDir: TGrpIconDir; 
    IconDirEntry: TGrpIconDirEntry; 

begin 
    //LoadIconWithScaleDown does high quality scaling and so we simply use it if it's available 
    ResourceNameWide := ResourceName; 
    if Succeeded(LoadIconWithScaleDown(HInstance, PWideChar(ResourceNameWide), IconSize, IconSize, Result)) then begin 
    exit; 
    end; 

    //XP: find the closest sized smaller icon and draw without stretching onto the centre of a canvas of the right size 
    Try 
    Stream := TResourceStream.Create(HInstance, ResourceName, RT_GROUP_ICON); 
    Try 
     Stream.Read(IconDir, SizeOf(IconDir)); 
     Assert(IconDir.idCount>0); 
     BestAvailableIconSize := high(BestAvailableIconSize); 
     for i := 0 to IconDir.idCount-1 do begin 
     Stream.Read(IconDirEntry, SizeOf(IconDirEntry)); 
     Assert(IconDirEntry.bWidth=IconDirEntry.bHeight); 
     ThisSize := IconDirEntry.bHeight; 
     if ThisSize=0 then begin//indicates a 256px icon 
      continue; 
     end; 
     if ThisSize=IconSize then begin 
      //a perfect match, no need to continue 
      Result := LoadImage(IconSize); 
      exit; 
     end else if ThisSize<IconSize then begin 
      //we're looking for the closest sized smaller icon 
      if BestAvailableIconSize<IconSize then begin 
      //we've already found one smaller 
      BestAvailableIconSize := Max(ThisSize, BestAvailableIconSize); 
      end else begin 
      //this is the first one that is smaller 
      BestAvailableIconSize := ThisSize; 
      end; 
     end; 
     end; 
     if BestAvailableIconSize<IconSize then begin 
     Result := CreateIconFromSmallerIcon(IconSize, LoadImage(BestAvailableIconSize)); 
     if Result<>0 then begin 
      exit; 
     end; 
     end; 
    Finally 
     FreeAndNil(Stream); 
    End; 
    Except 
    ;//swallow because this routine is contracted not to throw exceptions 
    End; 

    //final fallback: make do without 
    Result := 0; 
end; 

function LoadIconResourceMetric(const ResourceName: string; IconMetric: Integer): HICON; 
begin 
    Result := LoadIconResourceSize(ResourceName, IconSizeFromMetric(IconMetric)); 
end; 

end. 

इन फ़ंक्शंस का उपयोग करना काफी स्पष्ट है। वे मानते हैं कि संसाधन कोड के समान मॉड्यूल में स्थित है। अगर आपको सामान्यता के उस स्तर के लिए समर्थन की आवश्यकता होती है तो कोड को आसानी से HMODULE प्राप्त करने के लिए सामान्यीकृत किया जा सकता है।

कॉल LoadIconResourceMetric यदि आप सिस्टम के छोटे आकार या सिस्टम बड़े आइकन के बराबर आकार के आइकन लोड करना चाहते हैं तो कॉल करें।IconMetric पैरामीटर या तो ICON_SMALL या ICON_BIG होना चाहिए। टूलबार, मेनू और अधिसूचना आइकन के लिए, ICON_SMALL का उपयोग किया जाना चाहिए।

यदि आप पूर्ण शब्दों में आइकन आकार निर्दिष्ट करना चाहते हैं तो LoadIconResourceSize का उपयोग करें।

ये फ़ंक्शन HICON लौटाते हैं। आप निश्चित रूप से TIcon इंस्टेंस की संपत्ति को असाइन कर सकते हैं। अधिक संभावना है कि आप एक छवि सूची में जोड़ना चाहते हैं। ऐसा करने का सबसे आसान तरीका ImageList_AddIconTImageList उदाहरण के Handle को पार करना है।

नोट 1: डेल्फी के पुराने संस्करणों में LoadIconWithScaleDownCommCtrl में परिभाषित नहीं है। ऐसे डेल्फी संस्करणों के लिए आपको इसे लोड करने के लिए GetProcAddress पर कॉल करने की आवश्यकता है। ध्यान दें कि यह एक यूनिकोड केवल एपीआई है और इसलिए आपको इसे संसाधन नाम के लिए PWideChar भेजना होगा। इस तरह: LoadIconWithScaleDown(..., PWideChar(WideString(ResourceName)),...)

नोट 2:LoadIconWithScaleDown की परिभाषा त्रुटिपूर्ण है। यदि आप सामान्य नियंत्रण पुस्तकालय शुरू करने के बाद इसे कॉल करते हैं तो आपको कोई समस्या नहीं होगी। हालांकि, अगर आप अपनी प्रक्रिया के जीवन में जल्दी ही कार्य को कॉल करते हैं तो LoadIconWithScaleDown विफल हो सकता है। मैंने इस समस्या की रिपोर्ट करने के लिए अभी QC#101000 सबमिट कर दिया है। दोबारा, यदि आप इससे पीड़ित हैं तो आपको GetProcAddress स्वयं को कॉल करना होगा।

+7

इसे साझा करने के लिए धन्यवाद। बहुत उपयोगी। –

+0

क्या टर्मिनल ('MakeIntResource') के साथ 'LoadIconResourceMetric' को कॉल करने का कोई तरीका है, उदा। 'LoadIconResourceMetric (IDI_INFORMATION, ICON_SMALL)'? और क्या यह डी <= 2007 के लिए काम करता है? –

+0

@Ulrich 1. ordinals के लिए आपको 'स्ट्रिंग' पैरामीटर के बजाय 'पीसीहर' लेने के लिए इसे फिर से लिखना होगा। इससे मुझे अधिक सामान्य लगेगा। 2. डेल्फी के पुराने संस्करणों के लिए जिसमें 'CommCtrl' में 'LoadIconWithScaleDown' शामिल नहीं है, आपको अपने लिए लोड लाइब्रेरी/GetProcAddress नृत्य करने की आवश्यकता है। –

संबंधित मुद्दे