Android
RenderScript
खोज…
परिचय
रेंडरस्क्रिप्ट एक स्क्रिप्टिंग भाषा है जो आपको उच्च प्रदर्शन ग्राफिक रेंडरिंग और कच्चे कम्प्यूटेशनल कोड लिखने की अनुमति देती है। यह प्रदर्शन महत्वपूर्ण कोड लिखने का एक साधन प्रदान करता है जिसे सिस्टम बाद में उस प्रोसेसर के लिए मूल कोड के लिए संकलित करता है जिस पर वह चल सकता है। यह सीपीयू, एक मल्टी-कोर सीपीयू, या यहां तक कि जीपीयू भी हो सकता है। यह अंततः चलता है, कई कारकों पर निर्भर करता है जो डेवलपर को आसानी से उपलब्ध नहीं हैं, लेकिन यह भी निर्भर करता है कि आंतरिक मंच संकलक किस वास्तुकला का समर्थन करता है।
शुरू करना
RenderScript एंड्रॉइड पर उच्च प्रदर्शन समानांतर गणना की अनुमति देने के लिए एक रूपरेखा है। आपके द्वारा लिखे गए लिपियों को समानांतर में सभी उपलब्ध प्रोसेसर (जैसे सीपीयू, जीपीयू आदि) पर निष्पादित किया जाएगा, जिससे आप यह निर्धारित कर सकते हैं कि आप उस कार्य पर ध्यान केंद्रित करना चाहते हैं जो इसे निर्धारित और निष्पादित किया जाना है।
लिपियों को C99 आधारित भाषा में लिखा जाता है (C99 C प्रोग्रामिंग भाषा मानक का पुराना संस्करण है)। प्रत्येक स्क्रिप्ट के लिए एक जावा वर्ग बनाया जाता है जो आपको आसानी से अपने जावा कोड में RenderScript से बातचीत करने की अनुमति देता है।
अपना प्रोजेक्ट सेट करना
एंड्रॉइड फ्रेमवर्क लाइब्रेरी या सपोर्ट लाइब्रेरी के साथ, आपके ऐप में रेंडरस्क्रिप्ट तक पहुंचने के दो अलग-अलग तरीके मौजूद हैं। यहां तक कि अगर आप एपीआई स्तर 11 से पहले उपकरणों को लक्षित नहीं करना चाहते हैं, तो आपको हमेशा समर्थन लाइब्रेरी कार्यान्वयन का उपयोग करना चाहिए क्योंकि यह कई अलग-अलग उपकरणों में डिवाइस संगतता सुनिश्चित करता है। समर्थन पुस्तकालय कार्यान्वयन का उपयोग करने के लिए आपको कम से कम निर्माण उपकरण संस्करण 18.1.0
का उपयोग करने की आवश्यकता है!
अब अपने एप्लिकेशन की बिल्ड.gradle फ़ाइल को सेटअप करने देता है:
android {
compileSdkVersion 24
buildToolsVersion '24.0.1'
defaultConfig {
minSdkVersion 8
targetSdkVersion 24
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
}
}
-
renderscriptTargetApi
: यह संस्करण जल्द से जल्द एपीआई स्तर पर सेट होना चाहिए जो आपको आवश्यक सभी रेंडरस्क्रिप्ट कार्यक्षमता प्रदान करता है। -
renderscriptSupportModeEnabled
: यह समर्थन लाइब्रेरी रेंडरस्क्रिप्ट कार्यान्वयन के उपयोग को सक्षम करता है।
RenderScript कैसे काम करता है
एक विशिष्ट रेंडरस्क्रिप्ट में दो चीजें होती हैं: कर्नेल और फ़ंक्शंस। एक फ़ंक्शन ऐसा है जो इसे लगता है - यह एक इनपुट स्वीकार करता है, उस इनपुट के साथ कुछ करता है और आउटपुट देता है। एक कर्नेल वह जगह है जहां से रेंडरस्क्रिप्ट की वास्तविक शक्ति आती है।
कर्नेल एक फ़ंक्शन है जिसे एक Allocation
अंदर हर तत्व के खिलाफ निष्पादित किया जाता है। एक Allocation
का उपयोग Bitmap
या byte
सरणी जैसे डेटा को RenderScript
पास करने के लिए किया जा सकता है और उनका उपयोग कर्नेल से परिणाम प्राप्त करने के लिए भी किया जाता है। कर्नेल या तो एक Allocation
को इनपुट के रूप में और दूसरे को आउटपुट के रूप में ले सकते हैं या वे केवल एक Allocation
अंदर डेटा को संशोधित कर सकते हैं।
आप अपनी एक गुठली लिख सकते हैं, लेकिन कई पूर्वनिर्धारित गुठली भी हैं जिनका उपयोग आप गौसियन इमेज ब्लर की तरह सामान्य ऑपरेशन करने के लिए कर सकते हैं।
जैसा कि पहले से ही हर रेंडरस्क्रिप्ट फ़ाइल के लिए उल्लेख किया गया है एक वर्ग इसके साथ बातचीत करने के लिए उत्पन्न होता है। ये कक्षाएं हमेशा उपसर्ग ScriptC_
शुरू होती हैं और उसके बाद RenderScript फ़ाइल के नाम से शुरू होती हैं। उदाहरण के लिए यदि आपकी रेंडरस्क्रिप्ट फ़ाइल को example
कहा जाता है तो उत्पन्न जावा वर्ग को ScriptC_example
कहा ScriptC_example
। सभी पूर्वनिर्धारित लिपियाँ केवल उपसर्ग Script
शुरू होती हैं - उदाहरण के लिए गॉसियन इमेज ब्लर स्क्रिप्ट को ScriptIntrinsicBlur
कहा जाता है।
अपना पहला रेंडरस्क्रिप्ट लिखना
निम्न उदाहरण GitHub पर एक उदाहरण पर आधारित है। यह एक छवि की संतृप्ति को संशोधित करके बुनियादी छवि हेरफेर करता है। आप यहां स्रोत कोड पा सकते हैं और यह देख सकते हैं कि क्या आप इसे अपने साथ खेलना चाहते हैं। यहाँ एक त्वरित gif क्या परिणाम की तरह लग रहा है:
रेंडरस्क्रिप्ट बॉयलरप्लेट
आपकी परियोजना में फ़ोल्डर src/main/rs
में रेंडरस्क्रिप्ट फाइलें रहती हैं। प्रत्येक फ़ाइल में फ़ाइल एक्सटेंशन है .rs
और शीर्ष पर दो #pragma
कथन होने चाहिए:
#pragma version(1)
#pragma rs java_package_name(your.package.name)
#pragma version(1)
: इसका उपयोग आपके द्वारा उपयोग किए जा रहे रेंडरस्क्रिप्ट के संस्करण को सेट करने के लिए किया जा सकता है। वर्तमान में केवल संस्करण 1 है।#pragma rs java_package_name(your.package.name)
: इसका उपयोग इस विशेष रेंडरस्क्रिप्ट से बातचीत करने के लिए उत्पन्न जावा वर्ग के पैकेज नाम को सेट करने के लिए किया जा सकता है।
एक और #pragma
जिसे आपको आमतौर पर अपनी प्रत्येक रेंडरस्क्रिप्ट फ़ाइलों में सेट करना चाहिए और इसका उपयोग फ़्लोटिंग पॉइंट सटीक सेट करने के लिए किया जाता है। आप तीन अलग-अलग स्तरों पर फ़्लोटिंग पॉइंट सटीक सेट कर सकते हैं:
-
#pragma rs_fp_full
: यह उच्चतम परिशुद्धता के साथ सबसे सख्त सेटिंग है और यदि आप कुछ भी निर्दिष्ट करते हैं तो यह डिफ़ॉल्ट मान भी है। यदि आपको उच्च फ्लोटिंग पॉइंट परिशुद्धता की आवश्यकता है तो आपको इसका उपयोग करना चाहिए। -
#pragma rs_fp_relaxed
: यह उच्च फ़्लोटिंग पॉइंट परिशुद्धता के रूप में सुनिश्चित नहीं है, लेकिन कुछ आर्किटेक्चर पर यह अनुकूलन का एक गुच्छा सक्षम करता है जिससे आपकी स्क्रिप्ट तेज़ी से चल सकती हैं। -
#pragma rs_fp_imprecise
: यह और भी कम सटीकता सुनिश्चित करता है और इसका उपयोग किया जाना चाहिए यदि फ्लोटिंग पॉइंट सटीक वास्तव में आपकी स्क्रिप्ट के लिए मायने नहीं रखता है।
जब तक आप वास्तव में उच्च अस्थायी बिंदु परिशुद्धता की आवश्यकता नहीं करते हैं, तब अधिकांश स्क्रिप्ट #pragma rs_fp_relaxed
उपयोग कर सकते हैं।
सार्वत्रिक चर
अब सी कोड की तरह ही आप वैश्विक चर या स्थिरांक को परिभाषित कर सकते हैं:
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
float saturationLevel = 0.0f;
चर gMonoMult
प्रकार float3
। इसका मतलब यह एक वेक्टर है जिसमें 3 फ्लोट संख्या होती है। दूसरे float
वैरिएबल जिसे saturationValue
कहा जाता है, स्थिर नहीं है, इसलिए आप इसे रनटाइम पर उस मूल्य पर सेट कर सकते हैं जो आपको पसंद है। आप अपने कर्नेल या फ़ंक्शंस में चर का उपयोग कर सकते हैं और इसलिए वे आपके रेंडरस्क्रिप्ट से इनपुट देने या आउटपुट प्राप्त करने का एक और तरीका है। प्रत्येक निरंतर चर के लिए एक जावा और सेटर विधि संबंधित जावा वर्ग पर उत्पन्न नहीं होगी।
कर्नेल
लेकिन अब चलो कर्नेल को लागू करना शुरू करते हैं। इस उदाहरण के प्रयोजनों के लिए मैं कर्नेल में उपयोग किए गए गणित को छवि की संतृप्ति को संशोधित करने के लिए नहीं जा रहा हूं, लेकिन इसके बजाय एक कर्नेल को कैसे लागू किया जाए और इसे कैसे उपयोग किया जाए, इस पर ध्यान दिया जाएगा। इस अध्याय के अंत में मैं जल्दी बताऊंगा कि इस कर्नेल में कोड वास्तव में क्या कर रहा है।
सामान्य रूप में गुठली
आइए पहले स्रोत कोड पर एक नज़र डालें:
uchar4 __attribute__((kernel)) saturation(uchar4 in) {
float4 f4 = rsUnpackColor8888(in);
float3 dotVector = dot(f4.rgb, gMonoMult);
float3 newColor = mix(dotVector, f4.rgb, saturationLevel);
return rsPackColorTo8888(newColor);
}
जैसा कि आप देख सकते हैं कि यह एक अपवाद के साथ एक सामान्य सी फ़ंक्शन की तरह दिखता है: रिटर्न प्रकार और विधि नाम के बीच __attribute__((kernel))
। यह वही है जो रेंडरस्क्रिप्ट को बताता है कि यह विधि एक कर्नेल है। एक और बात जो आप देख सकते हैं कि यह विधि एक uchar4
पैरामीटर को स्वीकार करती है और एक और uchar4
मान uchar4
। uchar4
है - float3
वैरिएबल जैसा कि हमने पहले अध्याय में चर्चा की थी - एक वेक्टर। इसमें 4 uchar
मान शामिल हैं जो 0 से 255 तक की सीमा में केवल बाइट मान हैं।
आप इन व्यक्तिगत मूल्यों को कई अलग-अलग तरीकों से एक्सेस कर सकते हैं, उदाहरण के लिए in.r
बाइट लौटाएगा जो एक पिक्सेल के लाल चैनल से मेल खाती है। हम एक का उपयोग uchar4
के बाद से प्रत्येक पिक्सेल 4 मूल्यों से बना है - r
लाल, के लिए g
हरे रंग के लिए, b
नीला के लिए और a
अल्फा के लिए - और आप उन्हें इस आशुलिपि के साथ उपयोग कर सकते हैं। रेंडरस्क्रिप्ट आपको एक वेक्टर से किसी भी मान को लेने और उनके साथ एक और वेक्टर बनाने की अनुमति देता है। उदाहरण के लिए in.rgb
एक uchar3
मान uchar3
जिसमें अल्फ़ा मान के बिना पिक्सेल के लाल, हरे और नीले हिस्से होते हैं।
रनटाइम पर रेंडरस्क्रिप्ट एक छवि के प्रत्येक पिक्सेल के लिए इस कर्नेल विधि को कॉल करेगा, यही वजह है कि वापसी मूल्य और पैरामीटर सिर्फ एक uchar4
मान हैं। RenderScript इनमें से कई कॉल्स को सभी उपलब्ध प्रोसेसर पर समानांतर में चलाएगा, यही वजह है कि RenderScript इतना शक्तिशाली है। इसका मतलब यह भी है कि आपको थ्रेडिंग या थ्रेड सुरक्षा के बारे में चिंता करने की ज़रूरत नहीं है, आप बस प्रत्येक पिक्सेल के लिए जो भी करना चाहते हैं उसे लागू कर सकते हैं और रेंडरस्क्रिप्ट बाकी का ख्याल रखता है।
जावा में कर्नेल को कॉल करते समय आप दो Allocation
चर की आपूर्ति करते हैं, जिसमें एक इनपुट डेटा होता है और दूसरा जो आउटपुट प्राप्त करेगा। इनपुट Allocation
में प्रत्येक मूल्य के लिए आपकी कर्नेल पद्धति को बुलाया जाएगा और आउटपुट Allocation
में परिणाम लिखेगा।
RenderScript रनटाइम API तरीके
ऊपर कर्नेल में कुछ विधियों का उपयोग किया जाता है जो बॉक्स से बाहर प्रदान की जाती हैं। RenderScript कई ऐसे तरीके प्रदान करता है और वे लगभग कुछ भी आप RenderScript के साथ क्या करने जा रहे हैं के लिए महत्वपूर्ण हैं। उनमें से गणित के संचालन जैसे कि sin()
और हेल्पर mix()
जैसे mix()
तरीके हैं जो दो मूल्यों को दूसरे मूल्यों के अनुसार मिलाते हैं। लेकिन वैक्टर, क्वाटरन और मेट्रिसेस के साथ काम करते समय अधिक जटिल ऑपरेशन के तरीके भी हैं।
यदि आप किसी विशेष विधि के बारे में अधिक जानना चाहते हैं या एक विशिष्ट विधि की तलाश कर रहे हैं जो मैट्रिक्स के डॉट उत्पाद की गणना की तरह एक सामान्य ऑपरेशन करता है तो आधिकारिक रेंडरस्क्रिप्ट रनटाइम एपीआई संदर्भ वहां से सबसे अच्छा संसाधन है। आप इस दस्तावेज़ को यहां पा सकते हैं।
कर्नेल कार्यान्वयन
अब आइए एक नज़र डालें कि यह कर्नेल क्या कर रहा है। यहाँ कर्नेल में पहली पंक्ति है:
float4 f4 = rsUnpackColor8888(in);
पहली पंक्ति विधि में बनाया कॉल rsUnpackColor8888()
जो बदल देती है uchar4
एक के लिए मूल्य float4
मूल्यों। प्रत्येक रंग चैनल भी 0.0f - 1.0f
की सीमा में बदल जाता है, जहाँ 0.0f
0
और 1.0f
से 255
बाइट मान से मेल खाता है। इसका मुख्य उद्देश्य इस कर्नेल में सभी गणित को बहुत सरल बनाना है।
float3 dotVector = dot(f4.rgb, gMonoMult);
यह अगली पंक्ति दो वैक्टर के डॉट उत्पाद की गणना करने के लिए बिल्ट इन मेथड dot()
का उपयोग करती है। gMonoMult
एक निरंतर मूल्य है जिसे हमने ऊपर कुछ अध्यायों में परिभाषित किया है। चूँकि दोनों वैक्टर को डॉट उत्पाद की गणना करने के लिए एक ही लंबाई का होना चाहिए और यह भी कि जब से हम सिर्फ रंग चैनलों को प्रभावित करना चाहते हैं न कि किसी पिक्सेल के अल्फा चैनल को हम एक नए float3
वेक्टर को प्राप्त करने के लिए शॉर्टहैंड .rgb
का उपयोग करते हैं जिसमें बस शामिल होता है। लाल, हरा और नीला रंग चैनल। हम में से जो अभी भी स्कूल से याद करते हैं कि डॉट उत्पाद कैसे काम करता है, वे जल्दी से ध्यान देंगे कि डॉट उत्पाद को केवल एक मूल्य पर लौटना चाहिए न कि एक वेक्टर। फिर भी ऊपर के कोड में हम float3
वेक्टर को परिणाम बता रहे हैं। यह फिर से रेंडरस्क्रिप्ट की एक विशेषता है। जब आप एक वेक्टर को एक आयामी संख्या असाइन करते हैं तो वेक्टर में सभी तत्व इस मान पर सेट हो जाएंगे। उदाहरण के लिए निम्नलिखित स्निपेट float3
वेक्टर में तीन मूल्यों में से प्रत्येक को 2.0f
असाइन करेगा:
float3 example = 2.0f;
तो ऊपर दिए गए डॉट उत्पाद का परिणाम float3
वेक्टर में प्रत्येक तत्व को सौंपा गया है।
अब वह हिस्सा आता है जिसमें हम वास्तव में छवि के saturationLevel
को संशोधित करने के लिए वैश्विक चर saturationLevel
का उपयोग करते हैं:
float3 newColor = mix(dotVector, f4.rgb, saturationLevel);
यह हमारे द्वारा ऊपर निर्मित डॉट उत्पाद वेक्टर के साथ मूल रंग को एक साथ mix()
लिए बिल्ट इन मेथड mix()
का उपयोग करता है। उन्हें एक साथ कैसे मिलाया जाता है इसका निर्धारण वैश्विक saturationLevel
चर द्वारा किया जाता है। तो 0.0f
का एक saturationLevel
परिणाम मूल रंग मानों का कोई हिस्सा नहीं होने के कारण रंग का कारण होगा और इसमें केवल dotVector
में मान शामिल होंगे जिसके परिणामस्वरूप एक काले और सफेद या धूसर रंग की छवि होती है। 1.0f
मान के कारण परिणामी रंग पूरी तरह से मूल रंग मूल्यों से बना होगा और 1.0f
से ऊपर के मूल्य मूल रंगों को अधिक उज्ज्वल और तीव्र बनाने के लिए गुणा करेंगे।
return rsPackColorTo8888(newColor);
यह कर्नेल में अंतिम भाग है। rsPackColorTo8888()
float3
वेक्टर को फिर से लौटाए जाने वाले uchar4
मान में बदल देता है। परिणामी बाइट मानों को 0 और 255 के बीच की सीमा तक जकड़ दिया जाता है, इसलिए 1.0f
से अधिक के फ्लोट मानों का परिणाम 255 के बाइट मान और 0.0
से कम वाले मानों का परिणाम 0
बाइट मान में होगा।
और यह पूरे कर्नेल कार्यान्वयन है। अब केवल एक हिस्सा शेष है: जावा में एक कर्नेल को कैसे कॉल करें।
जावा में रेंडरस्क्रिप्ट को कॉल करना
मूल बातें
जैसा कि पहले से ही प्रत्येक RenderScript फ़ाइल के लिए ऊपर उल्लेख किया गया था एक जावा वर्ग उत्पन्न होता है जो आपको अपनी स्क्रिप्ट के साथ बातचीत करने की अनुमति देता है। इन फ़ाइलों में उपसर्ग ScriptC_
उसके बाद RenderScript फ़ाइल का नाम है। इन वर्गों का एक उदाहरण बनाने के लिए आपको पहले RenderScript
वर्ग की एक आवृत्ति की आवश्यकता है:
final RenderScript renderScript = RenderScript.create(context);
स्थिर विधि create()
का उपयोग Context
से एक RenderScript
उदाहरण बनाने के लिए किया जा सकता है। फिर आप उस जावा वर्ग को तुरंत बदल सकते हैं जो आपकी स्क्रिप्ट के लिए बनाया गया था। यदि आपने RenderScript फ़ाइल saturation.rs
को कॉल किया saturation.rs
तो क्लास को ScriptC_saturation
कहा ScriptC_saturation
:
final ScriptC_saturation script = new ScriptC_saturation(renderScript);
इस वर्ग पर अब आप संतृप्ति स्तर सेट कर सकते हैं और कर्नेल को कॉल कर सकते हैं। सेटर को saturationLevel
चर के लिए उत्पन्न किया गया था, जिसमें उपसर्ग set_
जिसके बाद चर का नाम होगा:
script.set_saturationLevel(1.0f);
get_
साथ एक उपसर्ग भी है जो आपको वर्तमान में सेट किए गए संतृप्ति स्तर को प्राप्त करने की अनुमति देता है:
float saturationLevel = script.get_saturationLevel();
कर्नेल आप अपने RenderScript में परिभाषित लगी होती हैं साथ forEach_
कर्नेल विधि के नाम के बाद। हमने जो कर्नेल लिखा है, वह एक इनपुट Allocation
और उसके मापदंडों के रूप में एक आउटपुट Allocation
अपेक्षा करता है:
script.forEach_saturation(inputAllocation, outputAllocation);
इनपुट Allocation
में इनपुट छवि समाहित करने की आवश्यकता है, और forEach_saturation
पद्धति के समाप्त होने के बाद आउटपुट आवंटन में संशोधित छवि डेटा शामिल होगा।
एक बार जब आपके पास एक Allocation
उदाहरण होता है, तो आप डेटा से कॉपी कर सकते हैं और उन Allocations
लिए तरीके copyFrom()
और copyTo()
। उदाहरण के लिए आप अपने इनपुट में एक नई छवि कॉपी कर सकते हैं `कॉल करके आवंटन:
inputAllocation.copyFrom(inputBitmap);
उसी तरह से आप आउटपुट Allocation
पर copyTo()
को कॉल करके परिणाम छवि प्राप्त कर सकते हैं:
outputAllocation.copyTo(outputBitmap);
आवंटन उदाहरण बनाना
एक Allocation
बनाने के कई तरीके हैं। एक बार जब आपके पास एक Allocation
उदाहरण होता है, तो आप ऊपर बताए गए जैसे copyTo()
और copyFrom()
साथ और उन Allocations
से नए डेटा को कॉपी कर सकते हैं, लेकिन उन्हें शुरू में बनाने के लिए आपको यह जानना होगा कि आप किस तरह के डेटा के साथ काम कर रहे हैं। चलो इनपुट Allocation
साथ शुरू करते हैं:
हम जल्दी से एक Bitmap
से हमारे इनपुट Allocation
बनाने के लिए स्थैतिक विधि createFromBitmap()
का उपयोग कर सकते हैं:
final Allocation inputAllocation = Allocation.createFromBitmap(renderScript, image);
इस उदाहरण में इनपुट छवि कभी नहीं बदलती है इसलिए हमें इनपुट Allocation
फिर से संशोधित करने की आवश्यकता नहीं है। नए आउटपुट Bitmap
को बनाने के लिए saturationLevel
परिवर्तन में हम हर बार इसका पुन: उपयोग कर सकते हैं।
आउटपुट Allocation
बनाना थोड़ा अधिक जटिल है। पहले हमें वह बनाने की आवश्यकता है जिसे Type
कहा जाता है। एक Type
का उपयोग किसी Allocation
साथ यह बताने के लिए किया जाता है कि वह किस प्रकार के डेटा के साथ काम कर रहा है। आमतौर पर एक का उपयोग करता है Type.Builder
वर्ग जल्दी से एक उपयुक्त बनाने के लिए Type
। आइए पहले कोड पर एक नज़र डालें:
final Type outputType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript))
.setX(inputBitmap.getWidth())
.setY(inputBitmap.getHeight())
.create();
हम 4 बिट चैनल के साथ एक सामान्य 32 बिट (या दूसरे शब्दों में 4 बाइट) पिक्सेल Bitmap
साथ काम कर रहे हैं। यही कारण है कि हम चुन रहे हैं है Element.RGBA_8888
बनाने के लिए Type
। फिर हम आउटपुट इमेज की चौड़ाई और ऊंचाई सेट करने के लिए इनपुट इमेज के समान तरीके setX()
और setY()
का उपयोग करते हैं। विधि create()
तब हमारे द्वारा निर्दिष्ट मापदंडों के साथ Type
बनाता है।
एक बार हमारे पास सही Type
हम स्थैतिक विधि से createTyped()
Allocation
बना सकते हैं।
final Allocation outputAllocation = Allocation.createTyped(renderScript, outputType);
अब हम लगभग हो चुके हैं। हमें एक आउटपुट Bitmap
की भी आवश्यकता है जिसमें हम आउटपुट Allocation
से डेटा की प्रतिलिपि बना सकते हैं। ऐसा करने के लिए हम एक ही आकार और कॉन्फ़िगरेशन के साथ एक नया खाली Bitmap
बनाने के लिए स्थैतिक विधि createBitmap()
का उपयोग करते हैं जो इनपुट Bitmap
रूप में होता है।
final Bitmap outputBitmap = Bitmap.createBitmap(
inputBitmap.getWidth(),
inputBitmap.getHeight(),
inputBitmap.getConfig()
);
और इसके साथ ही हमारे पास हमारे रेंडरस्क्रिप्ट को निष्पादित करने के लिए सभी पहेली टुकड़े हैं।
पूर्ण उदाहरण
अब एक उदाहरण में इस सब को एक साथ रखते हैं:
// Create the RenderScript instance
final RenderScript renderScript = RenderScript.create(context);
// Create the input Allocation
final Allocation inputAllocation = Allocation.createFromBitmap(renderScript, inputBitmap);
// Create the output Type.
final Type outputType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript))
.setX(inputBitmap.getWidth())
.setY(inputBitmap.getHeight())
.create();
// And use the Type to create am output Allocation
final Allocation outputAllocation = Allocation.createTyped(renderScript, outputType);
// Create an empty output Bitmap from the input Bitmap
final Bitmap outputBitmap = Bitmap.createBitmap(
inputBitmap.getWidth(),
inputBitmap.getHeight(),
inputBitmap.getConfig()
);
// Create an instance of our script
final ScriptC_saturation script = new ScriptC_saturation(renderScript);
// Set the saturation level
script.set_saturationLevel(2.0f);
// Execute the Kernel
script.forEach_saturation(inputAllocation, outputAllocation);
// Copy the result data to the output Bitmap
outputAllocation.copyTo(outputBitmap);
// Display the result Bitmap somewhere
someImageView.setImageBitmap(outputBitmap);
निष्कर्ष
इस परिचय के साथ, आपको सरल छवि हेरफेर के लिए अपने स्वयं के रेंडरस्क्रिप्ट कर्नल्स लिखने के लिए तैयार होना चाहिए। हालाँकि कुछ बातें हैं जिन्हें आपको ध्यान में रखना है:
- RenderScript केवल एप्लिकेशन प्रोजेक्ट्स में काम करता है : वर्तमान में RenderScript फाइलें एक लाइब्रेरी प्रोजेक्ट का हिस्सा नहीं हो सकती हैं।
- मेमोरी के लिए देखें : रेंडरस्क्रिप्ट बहुत तेज़ है, लेकिन यह मेमोरी इंटेंसिव भी हो सकता है। कभी भी
RenderScript
एक से अधिक उदाहरण नहीं होने चाहिए। आपको जितना संभव हो उतना पुन: उपयोग करना चाहिए। आम तौर पर आपको बस एक बार अपनेAllocation
उदाहरण बनाने की आवश्यकता होती है और भविष्य में उनका पुन: उपयोग कर सकते हैं। वही आउटपुटBitmaps
या आपके स्क्रिप्ट उदाहरणों के लिए जाता है। जितना संभव हो उतना पुन: उपयोग करें। - अपना काम पृष्ठभूमि में करें : फिर से रेंडरस्क्रिप्ट बहुत तेज़ है, लेकिन किसी भी तरह से तुरंत नहीं। किसी भी कर्नेल, विशेष रूप से जटिल लोगों को यूआई थ्रेड को एक
AsyncTask
या कुछ समान में निष्पादित किया जाना चाहिए। हालाँकि अधिकांश भाग के लिए आपको मेमोरी लीक के बारे में चिंता करने की आवश्यकता नहीं है। सभी RenderScript संबंधित कक्षाएं केवल एप्लिकेशनContext
उपयोग करती हैं और इसलिए मेमोरी लीक का कारण नहीं बनती हैं। लेकिन आपको अभी भी सामान्य चीज़ों के बारे में चिंता करना होगा जैसे किView
,Activity
या किसी भीContext
लीक करना जो आप स्वयं का उपयोग करते हैं! - निर्मित सामान का उपयोग करें : कई पूर्वनिर्धारित स्क्रिप्ट हैं जो छवि धुंधला, सम्मिश्रण, परिवर्तित, आकार बदलने जैसे कार्य करती हैं। और कई और तरीके हैं जो आपकी गुठली को लागू करने में मदद करते हैं। संभावना यह है कि यदि आप कुछ करना चाहते हैं तो या तो एक स्क्रिप्ट या विधि है जो पहले से ही वह है जो आप करने की कोशिश कर रहे हैं। पहिया को सुदृढ़ मत करो।
यदि आप जल्दी से आरंभ करना चाहते हैं और वास्तविक कोड के साथ खेलना चाहते हैं, तो मैं आपको उदाहरण GitHub प्रोजेक्ट पर एक नज़र डालने की सलाह देता हूं, जो इस ट्यूटोरियल में बताए गए सटीक उदाहरण को लागू करता है। आप यहां प्रोजेक्ट पा सकते हैं। RenderScript के साथ मज़े करो!
एक छवि को धुंधला करें
यह उदाहरण दर्शाता है कि एक छवि को धुंधला करने के लिए Renderscript एपीआई का उपयोग कैसे करें (बिटमैप का उपयोग करके)। यह उदाहरण android Renderscript API (API> = 17) द्वारा प्रदान ScriptInstrinsicBlur का उपयोग करता है।
public class BlurProcessor {
private RenderScript rs;
private Allocation inAllocation;
private Allocation outAllocation;
private int width;
private int height;
private ScriptIntrinsicBlur blurScript;
public BlurProcessor(RenderScript rs) {
this.rs = rs;
}
public void initialize(int width, int height) {
blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
blurScript.setRadius(7f); // Set blur radius. 25 is max
if (outAllocation != null) {
outAllocation.destroy();
outAllocation = null;
}
// Bitmap must have ARGB_8888 config for this type
Type bitmapType = new Type.Builder(rs, Element.RGBA_8888(rs))
.setX(width)
.setY(height)
.setMipmaps(false) // We are using MipmapControl.MIPMAP_NONE
.create();
// Create output allocation
outAllocation = Allocation.createTyped(rs, bitmapType);
// Create input allocation with same type as output allocation
inAllocation = Allocation.createTyped(rs, bitmapType);
}
public void release() {
if (blurScript != null) {
blurScript.destroy();
blurScript = null;
}
if (inAllocation != null) {
inAllocation.destroy();
inAllocation = null;
}
if (outAllocation != null) {
outAllocation.destroy();
outAllocation = null;
}
}
public Bitmap process(Bitmap bitmap, boolean createNewBitmap) {
if (bitmap.getWidth() != width || bitmap.getHeight() != height) {
// Throw error if required
return null;
}
// Copy data from bitmap to input allocations
inAllocation.copyFrom(bitmap);
// Set input for blur script
blurScript.setInput(inAllocation);
// process and set data to the output allocation
blurScript.forEach(outAllocation);
if (createNewBitmap) {
Bitmap returnVal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
outAllocation.copyTo(returnVal);
return returnVal;
}
outAllocation.copyTo(bitmap);
return bitmap;
}
}
प्रत्येक स्क्रिप्ट में एक कर्नेल होता है जो डेटा को संसाधित करता है और इसे आमतौर पर forEach
पद्धति के माध्यम से लागू किया जाता है।
public class BlurActivity extends AppCompatActivity {
private BlurProcessor blurProcessor;
@Override
public void onCreate(Bundle savedInstanceState) {
// setup layout and other stuff
blurProcessor = new BlurProcessor(Renderscript.create(getApplicationContext()));
}
private void loadImage(String path) {
// Load image to bitmap
Bitmap bitmap = loadBitmapFromPath(path);
// Initialize processor for this bitmap
blurProcessor.release();
blurProcessor.initialize(bitmap.getWidth(), bitmap.getHeight());
// Blur image
Bitmap blurImage = blurProcessor.process(bitmap, true); // Use newBitamp as false if you don't want to create a new bitmap
}
}
इसका उदाहरण यहां दिया गया है। प्रसंस्करण को पृष्ठभूमि थ्रेड में करने की सलाह दी जाती है।
एक दृश्य धुंधला
BlurBitmapTask.java
public class BlurBitmapTask extends AsyncTask<Bitmap, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private final RenderScript renderScript;
private boolean shouldRecycleSource = false;
public BlurBitmapTask(@NonNull Context context, @NonNull ImageView imageView) {
// Use a WeakReference to ensure
// the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
renderScript = RenderScript.create(context);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Bitmap... params) {
Bitmap bitmap = params[0];
return blurBitmap(bitmap);
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap == null || isCancelled()) {
return;
}
final ImageView imageView = imageViewReference.get();
if (imageView == null) {
return;
}
imageView.setImageBitmap(bitmap);
}
public Bitmap blurBitmap(Bitmap bitmap) {
// https://plus.google.com/+MarioViviani/posts/fhuzYkji9zz
//Let's create an empty bitmap with the same size of the bitmap we want to blur
Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
//Instantiate a new Renderscript
//Create an Intrinsic Blur Script using the Renderscript
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
//Create the in/out Allocations with the Renderscript and the in/out bitmaps
Allocation allIn = Allocation.createFromBitmap(renderScript, bitmap);
Allocation allOut = Allocation.createFromBitmap(renderScript, outBitmap);
//Set the radius of the blur
blurScript.setRadius(25.f);
//Perform the Renderscript
blurScript.setInput(allIn);
blurScript.forEach(allOut);
//Copy the final bitmap created by the out Allocation to the outBitmap
allOut.copyTo(outBitmap);
// recycle the original bitmap
// nope, we are using the original bitmap as well :/
if (shouldRecycleSource) {
bitmap.recycle();
}
//After finishing everything, we destroy the Renderscript.
renderScript.destroy();
return outBitmap;
}
public boolean isShouldRecycleSource() {
return shouldRecycleSource;
}
public void setShouldRecycleSource(boolean shouldRecycleSource) {
this.shouldRecycleSource = shouldRecycleSource;
}
}
उपयोग:
ImageView imageViewOverlayOnViewToBeBlurred
.setImageDrawable(ContextCompat.getDrawable(this, android.R.color.transparent));
View viewToBeBlurred.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW);
viewToBeBlurred.setDrawingCacheEnabled(true);
BlurBitmapTask blurBitmapTask = new BlurBitmapTask(this, imageViewOverlayOnViewToBeBlurred);
blurBitmapTask.execute(Bitmap.createBitmap(viewToBeBlurred.getDrawingCache()));
viewToBeBlurred.setDrawingCacheEnabled(false);