গ্রাফিক্স মডিফায়ার

Canvas কম্পোজেবল ছাড়াও, কম্পোজে বেশ কিছু দরকারি গ্রাফিক্স Modifiers রয়েছে যা কাস্টম কন্টেন্ট আঁকতে সাহায্য করে। এই মডিফায়ারগুলো দরকারি, কারণ এগুলো যেকোনো কম্পোজেবলের ওপর প্রয়োগ করা যায়।

অঙ্কন সংশোধক

কম্পোজে সমস্ত ড্রয়িং কমান্ড একটি ড্রয়িং মডিফায়ারের মাধ্যমে করা হয়। কম্পোজে তিনটি প্রধান ড্রয়িং মডিফায়ার রয়েছে:

ড্রয়িং-এর জন্য মূল মডিফায়ার হলো drawWithContent , যেখানে আপনি আপনার Composable-এর ড্রয়িং অর্ডার এবং মডিফায়ারের ভিতরে জারি করা ড্রয়িং কমান্ডগুলো নির্ধারণ করতে পারেন। drawBehind হলো drawWithContent এর একটি সুবিধাজনক র‍্যাপার, যেখানে ড্রয়িং অর্ডারটি Composable-এর কন্টেন্টের পিছনে সেট করা থাকে। drawWithCache এর ভিতরে onDrawBehind অথবা onDrawWithContent কল করে এবং এগুলোর মধ্যে তৈরি হওয়া অবজেক্টগুলোকে ক্যাশ করার একটি ব্যবস্থা প্রদান করে।

Modifier.drawWithContent : অঙ্কনের ক্রম নির্বাচন করুন

Modifier.drawWithContent আপনাকে কম্পোজেবলের কন্টেন্টের আগে বা পরে DrawScope অপারেশনগুলো সম্পাদন করার সুযোগ দেয়। কম্পোজেবলের আসল কন্টেন্ট রেন্ডার করার জন্য অবশ্যই drawContent কল করতে হবে। এই মডিফায়ারের সাহায্যে, আপনি অপারেশনগুলোর ক্রম নির্ধারণ করতে পারেন, অর্থাৎ আপনার কন্টেন্টটি কাস্টম ড্রয়িং অপারেশনের আগে নাকি পরে আঁকা হবে তা ঠিক করতে পারেন।

উদাহরণস্বরূপ, যদি আপনি UI-তে একটি ফ্ল্যাশলাইট কীহোল এফেক্ট তৈরি করার জন্য আপনার কন্টেন্টের উপরে একটি রেডিয়াল গ্রেডিয়েন্ট রেন্ডার করতে চান, তাহলে আপনি নিম্নলিখিত কাজটি করতে পারেন:

var pointerOffset by remember {
    mutableStateOf(Offset(0f, 0f))
}
Column(
    modifier = Modifier
        .fillMaxSize()
        .pointerInput("dragging") {
            detectDragGestures { change, dragAmount ->
                pointerOffset += dragAmount
            }
        }
        .onSizeChanged {
            pointerOffset = Offset(it.width / 2f, it.height / 2f)
        }
        .drawWithContent {
            drawContent()
            // draws a fully black area with a small keyhole at pointerOffset that’ll show part of the UI.
            drawRect(
                Brush.radialGradient(
                    listOf(Color.Transparent, Color.Black),
                    center = pointerOffset,
                    radius = 100.dp.toPx(),
                )
            )
        }
) {
    // Your composables here
}

চিত্র ১ : একটি Composable-এর উপরে Modifier.drawWithContent ব্যবহার করে ফ্ল্যাশলাইটের মতো একটি UI অভিজ্ঞতা তৈরি করা হয়েছে।

Modifier.drawBehind : একটি কম্পোজেবলের পিছনে অঙ্কন করা

Modifier.drawBehind আপনাকে স্ক্রিনে অঙ্কিত কম্পোজেবল কন্টেন্টের পিছনে DrawScope অপারেশন সম্পাদন করতে দেয়। আপনি যদি Canvas এর ইমপ্লিমেন্টেশনটি দেখেন, তাহলে হয়তো লক্ষ্য করবেন যে এটি Modifier.drawBehind এর একটি সুবিধাজনক র‍্যাপার মাত্র।

Text পিছনে একটি গোলাকার আয়তক্ষেত্র আঁকতে:

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawBehind {
            drawRoundRect(
                Color(0xFFBBAAEE),
                cornerRadius = CornerRadius(10.dp.toPx())
            )
        }
        .padding(4.dp)
)

যার ফলে নিম্নলিখিত ফলাফল পাওয়া যায়:

Modifier.drawBehind ব্যবহার করে টেক্সট এবং একটি ব্যাকগ্রাউন্ড আঁকা হয়েছে।
চিত্র ২ : Modifier.drawBehind ব্যবহার করে আঁকা লেখা ও পটভূমি।

Modifier.drawWithCache : ড্র অবজেক্ট অঙ্কন এবং ক্যাশ করা

Modifier.drawWithCache এর ভিতরে তৈরি হওয়া অবজেক্টগুলোকে ক্যাশ করে রাখে। ড্রয়িং এলাকার আকার একই থাকা পর্যন্ত, অথবা পঠিত কোনো স্টেট অবজেক্ট পরিবর্তিত না হওয়া পর্যন্ত অবজেক্টগুলো ক্যাশ করা থাকে। এই মডিফায়ারটি ড্রয়িং কলের পারফরম্যান্স উন্নত করার জন্য উপযোগী, কারণ এটি ড্র করার সময় তৈরি হওয়া অবজেক্টগুলোকে (যেমন: Brush, Shader, Path ইত্যাদি) পুনরায় বরাদ্দ করার প্রয়োজনীয়তা এড়ায়।

বিকল্পভাবে, আপনি মডিফায়ারের বাইরে remember ব্যবহার করে অবজেক্ট ক্যাশ করতে পারেন। তবে, এটি সবসময় সম্ভব নয়, কারণ আপনার কাছে সবসময় কম্পোজিশনটির অ্যাক্সেস থাকে না। যদি অবজেক্টগুলো শুধুমাত্র আঁকার জন্য ব্যবহৃত হয়, তবে drawWithCache ব্যবহার করা আরও বেশি পারফরম্যান্ট হতে পারে।

উদাহরণস্বরূপ, যদি আপনি কোনো Text পিছনে গ্রেডিয়েন্ট আঁকার জন্য একটি Brush তৈরি করেন, তাহলে drawWithCache ব্যবহার করলে ড্রয়িং এলাকার আকার পরিবর্তিত না হওয়া পর্যন্ত Brush অবজেক্টটি ক্যাশে করা থাকে:

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawWithCache {
            val brush = Brush.linearGradient(
                listOf(
                    Color(0xFF9E82F0),
                    Color(0xFF42A5F5)
                )
            )
            onDrawBehind {
                drawRoundRect(
                    brush,
                    cornerRadius = CornerRadius(10.dp.toPx())
                )
            }
        }
)

drawWithCache ব্যবহার করে ব্রাশ অবজেক্ট ক্যাশ করা
চিত্র ৩ : drawWithCache ব্যবহার করে ব্রাশ অবজেক্ট ক্যাশ করা

গ্রাফিক্স মডিফায়ার

Modifier.graphicsLayer : কম্পোজেবলগুলিতে রূপান্তর প্রয়োগ করুন

Modifier.graphicsLayer হলো এমন একটি মডিফায়ার যা কম্পোজেবল ড্র-এর কন্টেন্টকে একটি ড্র লেয়ারে পরিণত করে। একটি লেয়ার বিভিন্ন ধরনের কাজ করে থাকে, যেমন:

  • এর ড্রয়িং নির্দেশাবলীর জন্য আইসোলেশন ( RenderNode এর অনুরূপ)। একটি লেয়ারের অংশ হিসাবে ধারণ করা ড্রয়িং নির্দেশাবলী অ্যাপ্লিকেশন কোড পুনরায় এক্সিকিউট না করেই রেন্ডারিং পাইপলাইন দ্বারা দক্ষতার সাথে পুনরায় জারি করা যেতে পারে।
  • এমন রূপান্তর যা একটি লেয়ারের অন্তর্ভুক্ত সমস্ত অঙ্কন নির্দেশাবলীর উপর প্রযোজ্য।
  • কম্পোজিশন ক্ষমতার জন্য র‍্যাস্টারাইজেশন। যখন একটি লেয়ারকে র‍্যাস্টারাইজ করা হয়, তখন এর ড্রয়িং নির্দেশাবলী কার্যকর করা হয় এবং আউটপুটটি একটি অফস্ক্রিন বাফারে ধারণ করা হয়। পরবর্তী ফ্রেমগুলোর জন্য এই ধরনের বাফার কম্পোজিট করা পৃথক নির্দেশাবলী কার্যকর করার চেয়ে দ্রুততর, কিন্তু স্কেলিং বা রোটেশনের মতো ট্রান্সফর্ম প্রয়োগ করা হলে এটি একটি বিটম্যাপের মতো আচরণ করবে।

রূপান্তর

Modifier.graphicsLayer তার ড্রয়িং নির্দেশাবলীকে বিচ্ছিন্ন রাখে; উদাহরণস্বরূপ, Modifier.graphicsLayer ব্যবহার করে বিভিন্ন রূপান্তর প্রয়োগ করা যেতে পারে। ড্রয়িং ল্যাম্বডা পুনরায় কার্যকর করার প্রয়োজন ছাড়াই এগুলিকে অ্যানিমেট বা পরিবর্তন করা সম্ভব।

Modifier.graphicsLayer আপনার কম্পোজেবলের পরিমাপকৃত আকার বা অবস্থান পরিবর্তন করে না, কারণ এটি শুধুমাত্র ড্র ফেজকে প্রভাবিত করে। এর মানে হলো, আপনার কম্পোজেবলটি যদি তার লেআউট সীমার বাইরে ড্র হয়, তবে এটি অন্যগুলোর উপর ওভারল্যাপ করতে পারে।

এই মডিফায়ারটির সাহায্যে নিম্নলিখিত রূপান্তরগুলি প্রয়োগ করা যেতে পারে:

স্কেল - আকার বৃদ্ধি করুন

scaleX এবং scaleY যথাক্রমে অনুভূমিক বা উল্লম্ব দিকে বিষয়বস্তুকে বড় বা ছোট করে। 1.0f মানটি স্কেলে কোনো পরিবর্তন না হওয়া নির্দেশ করে, এবং 0.5f মানটি আকারের অর্ধেক হওয়া বোঝায়।

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.scaleX = 1.2f
            this.scaleY = 0.8f
        }
)

চিত্র ৪ : একটি কম্পোজেবল ইমেজে scaleX এবং scaleY এর প্রয়োগ
অনুবাদ

graphicsLayer ব্যবহার করে translationX এবং translationY পরিবর্তন করা যায়। translationX কম্পোজেবলটিকে বামে বা ডানে সরায়। translationY কম্পোজেবলটিকে উপরে বা নিচে সরায়।

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.translationX = 100.dp.toPx()
            this.translationY = 10.dp.toPx()
        }
)

চিত্র ৫ : Modifier.graphicsLayer ব্যবহার করে ইমেজে translationX এবং translationY প্রয়োগ।
ঘূর্ণন

অনুভূমিকভাবে ঘোরানোর জন্য rotationX , উল্লম্বভাবে ঘোরানোর জন্য rotationY এবং Z অক্ষ বরাবর ঘোরানোর জন্য rotationZ (সাধারণ ঘূর্ণন) সেট করুন। এই মানটি ডিগ্রিতে (০-৩৬০) নির্দিষ্ট করা হয়।

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

চিত্র ৬ : Modifier.graphicsLayer দ্বারা ইমেজে সেট করা rotationX, rotationY এবং rotationZ।
উৎপত্তি

একটি transformOrigin নির্দিষ্ট করা যেতে পারে। এরপর এটি সেই বিন্দু হিসেবে ব্যবহৃত হয় যেখান থেকে রূপান্তরগুলো সংঘটিত হয়। এখন পর্যন্ত সমস্ত উদাহরণে TransformOrigin.Center ব্যবহার করা হয়েছে, যা (0.5f, 0.5f) বিন্দুতে অবস্থিত। যদি আপনি অরিজিনটি (0f, 0f) বিন্দুতে নির্দিষ্ট করেন, তাহলে রূপান্তরগুলো কম্পোজেবলটির উপরের-বাম কোণা থেকে শুরু হবে।

যদি আপনি rotationZ ট্রান্সফরমেশন দিয়ে মূলবিন্দু পরিবর্তন করেন, তাহলে দেখতে পাবেন যে আইটেমটি কম্পোজেবলের উপরের বাম দিককে কেন্দ্র করে ঘোরে:

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.transformOrigin = TransformOrigin(0f, 0f)
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

চিত্র ৭ : TransformOrigin 0f, 0f এ সেট করে ঘূর্ণন প্রয়োগ করা হয়েছে

ক্লিপ এবং আকৃতি

`Shape` সেই রূপরেখাটি নির্দিষ্ট করে, যার মধ্যে clip = true হলে কন্টেন্টটি ক্লিপ হবে। এই উদাহরণে, আমরা দুটি বক্সের জন্য দুটি ভিন্ন ক্লিপ সেট করেছি - একটি graphicsLayer `clip` ভেরিয়েবল ব্যবহার করে, এবং অন্যটি Modifier.clip সুবিধাজনক র‍্যাপারটি ব্যবহার করে।

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .size(200.dp)
            .graphicsLayer {
                clip = true
                shape = CircleShape
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }
    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(CircleShape)
            .background(Color(0xFF4DB6AC))
    )
}

প্রথম বক্সের বিষয়বস্তু (“Hello Compose” লেখাটি) বৃত্তাকার আকৃতিতে ছেঁটে ফেলা হয়েছে:

বাক্সে সংযুক্ত ক্লিপটি গঠনযোগ্য
চিত্র ৮ : বক্স কম্পোজেবলে প্রয়োগ করা ক্লিপ

এরপর যদি আপনি উপরের গোলাপী বৃত্তটিতে একটি translationY প্রয়োগ করেন, তাহলে দেখবেন যে Composable-টির সীমানা একই থাকে, কিন্তু বৃত্তটি নিচের বৃত্তটির নিচে (এবং এর সীমানার বাইরে) আঁকা হয়।

ট্রান্সলেশনY দিয়ে ক্লিপ প্রয়োগ করা হয়েছে, এবং আউটলাইনের জন্য লাল বর্ডার দেওয়া হয়েছে।
চিত্র ৯ : ট্রান্সলেশনY সহ ক্লিপ প্রয়োগ করা হয়েছে, এবং আউটলাইনের জন্য লাল বর্ডার দেওয়া হয়েছে।

কম্পোজেবলটিকে যে অঞ্চলে আঁকা হয়েছে, সেই অঞ্চলের মধ্যে সীমাবদ্ধ রাখতে, আপনি মডিফায়ার চেইনের শুরুতে আরেকটি Modifier.clip(RectangleShape) যোগ করতে পারেন। এর ফলে কন্টেন্টটি মূল সীমানার মধ্যেই থেকে যায়।

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .clip(RectangleShape)
            .size(200.dp)
            .border(2.dp, Color.Black)
            .graphicsLayer {
                clip = true
                shape = CircleShape
                translationY = 50.dp.toPx()
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }

    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(RoundedCornerShape(500.dp))
            .background(Color(0xFF4DB6AC))
    )
}

গ্রাফিক্সলেয়ার রূপান্তরের উপরে ক্লিপ প্রয়োগ করা হয়েছে
চিত্র ১০ : গ্রাফিক্স লেয়ার রূপান্তরের উপরে ক্লিপ প্রয়োগ

আলফা

Modifier.graphicsLayer ব্যবহার করে পুরো লেয়ারটির জন্য একটি alpha (অস্বচ্ছতা) নির্ধারণ করা যায়। 1.0f মানে সম্পূর্ণ অস্বচ্ছ এবং 0.0f মানে অদৃশ্য।

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "clock",
    modifier = Modifier
        .graphicsLayer {
            this.alpha = 0.5f
        }
)

আলফা প্রয়োগ করা ছবি
চিত্র ১১ : আলফা প্রয়োগ করা ছবি

কম্পোজিটিং কৌশল

আলফা এবং স্বচ্ছতা নিয়ে কাজ করাটা শুধুমাত্র একটি আলফা মান পরিবর্তন করার মতো সহজ নাও হতে পারে। আলফা পরিবর্তন করার পাশাপাশি, একটি graphicsLayer CompositingStrategy (CompositingStrategy) সেট করার বিকল্পও রয়েছে। একটি CompositingStrategy নির্ধারণ করে যে, কম্পোজেবলের বিষয়বস্তু স্ক্রিনে আগে থেকে আঁকা অন্যান্য বিষয়বস্তুর সাথে কীভাবে কম্পোজিট (একত্রিত) করা হবে।

বিভিন্ন কৌশলগুলো হলো:

স্বয়ংক্রিয় (ডিফল্ট)

কম্পোজিটিং স্ট্র্যাটেজিটি graphicsLayer এর বাকি প্যারামিটারগুলো দ্বারা নির্ধারিত হয়। যদি আলফা 1.0f-এর কম হয় অথবা কোনো RenderEffect সেট করা থাকে, তবে এটি লেয়ারটিকে একটি অফস্ক্রিন বাফারে রেন্ডার করে। যখনই আলফা 1f-এর কম হয়, তখন বিষয়বস্তু রেন্ডার করার জন্য এবং তারপর এই অফস্ক্রিন বাফারটিকে সংশ্লিষ্ট আলফাসহ গন্তব্যে ড্র করার জন্য স্বয়ংক্রিয়ভাবে একটি কম্পোজিটিং লেয়ার তৈরি হয়। সেট করা CompositingStrategy নির্বিশেষে, একটি RenderEffect বা ওভারস্ক্রোল সেট করলে বিষয়বস্তু সর্বদা একটি অফস্ক্রিন বাফারে রেন্ডার হয়।

অফস্ক্রিন

গন্তব্যে রেন্ডার করার আগে, কম্পোজেবলের বিষয়বস্তু সর্বদা একটি অফস্ক্রিন টেক্সচার বা বিটম্যাপে র‍্যাস্টারাইজ করা হয়। এটি কন্টেন্ট মাস্ক করার জন্য BlendMode অপারেশন প্রয়োগ করতে এবং জটিল ড্রয়িং নির্দেশাবলীর সেট রেন্ডার করার সময় পারফরম্যান্সের জন্য উপযোগী।

CompositingStrategy.Offscreen ব্যবহারের একটি উদাহরণ হলো BlendModes । নিচের উদাহরণটি দেখুন, ধরুন আপনি BlendMode.Clear ব্যবহার করে একটি ড্র কমান্ড দিয়ে একটি Image composable-এর কিছু অংশ মুছে ফেলতে চান। যদি আপনি compositingStrategy কে CompositingStrategy.Offscreen এ সেট না করেন, তাহলে BlendMode এর নিচের সমস্ত কন্টেন্টের সাথে ইন্টারঅ্যাক্ট করবে।

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = "Dog",
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(120.dp)
        .aspectRatio(1f)
        .background(
            Brush.linearGradient(
                listOf(
                    Color(0xFFC5E1A5),
                    Color(0xFF80DEEA)
                )
            )
        )
        .padding(8.dp)
        .graphicsLayer {
            compositingStrategy = CompositingStrategy.Offscreen
        }
        .drawWithCache {
            val path = Path()
            path.addOval(
                Rect(
                    topLeft = Offset.Zero,
                    bottomRight = Offset(size.width, size.height)
                )
            )
            onDrawWithContent {
                clipPath(path) {
                    // this draws the actual image - if you don't call drawContent, it wont
                    // render anything
                    this@onDrawWithContent.drawContent()
                }
                val dotSize = size.width / 8f
                // Clip a white border for the content
                drawCircle(
                    Color.Black,
                    radius = dotSize,
                    center = Offset(
                        x = size.width - dotSize,
                        y = size.height - dotSize
                    ),
                    blendMode = BlendMode.Clear
                )
                // draw the red circle indication
                drawCircle(
                    Color(0xFFEF5350), radius = dotSize * 0.8f,
                    center = Offset(
                        x = size.width - dotSize,
                        y = size.height - dotSize
                    )
                )
            }
        }
)

CompositingStrategy Offscreen এ সেট করলে, এটি কমান্ডগুলো কার্যকর করার জন্য একটি অফস্ক্রিন টেক্সচার তৈরি করে (শুধুমাত্র এই কম্পোজেবলের বিষয়বস্তুর উপর BlendMode প্রয়োগ করে)। এরপর এটি স্ক্রিনে আগে থেকে রেন্ডার করা বিষয়বস্তুর উপরে সেটিকে রেন্ডার করে, এবং ইতিমধ্যে আঁকা বিষয়বস্তুকে প্রভাবিত করে না।

অ্যাপের ভিতরে BlendMode.Clear চালু থাকা অবস্থায়, বৃত্তাকার ইঙ্গিত প্রদর্শনকারী একটি ইমেজে Modifier.drawWithContent প্রয়োগ করা হচ্ছে।
চিত্র ১২ : অ্যাপের ভিতরে BlendMode.Clear এবং CompositingStrategy.Offscreen ব্যবহার করে একটি ইমেজের উপর Modifier.drawWithContent প্রয়োগ করে একটি বৃত্তাকার ইঙ্গিত দেখানো হচ্ছে।

আপনি যদি CompositingStrategy.Offscreen ব্যবহার না করে থাকেন, তাহলে BlendMode.Clear প্রয়োগ করলে গন্তব্যের সমস্ত পিক্সেল মুছে যায়, আগে থেকে যা-ই সেট করা থাকুক না কেন—এবং উইন্ডোর রেন্ডারিং বাফার (কালো) দৃশ্যমান থাকে। আলফা-সম্পর্কিত অনেক BlendModes অফস্ক্রিন বাফার ছাড়া প্রত্যাশিতভাবে কাজ করবে না। লাল বৃত্ত নির্দেশকের চারপাশে কালো বলয়টি লক্ষ্য করুন:

একটি বৃত্তাকার ইঙ্গিত প্রদর্শনকারী ইমেজের উপর Modifier.drawWithContent প্রয়োগ করা হচ্ছে, যেখানে BlendMode.Clear এবং কোনো CompositingStrategy সেট করা নেই।
চিত্র ১৩ : একটি ইমেজের উপর Modifier.drawWithContent ব্যবহার করে একটি বৃত্তাকার ইঙ্গিত দেখানো হচ্ছে, যেখানে BlendMode.Clear ব্যবহৃত হয়েছে এবং কোনো CompositingStrategy সেট করা নেই।

বিষয়টি আরেকটু ভালোভাবে বোঝার জন্য: যদি অ্যাপটির একটি স্বচ্ছ উইন্ডো ব্যাকগ্রাউন্ড থাকতো, এবং আপনি CompositingStrategy.Offscreen ব্যবহার না করতেন, তাহলে BlendMode পুরো অ্যাপটির সাথেই কাজ করতো। এটি নীচের অ্যাপ বা ওয়ালপেপারটি দেখানোর জন্য সমস্ত পিক্সেল মুছে ফেলতো, যেমনটা এই উদাহরণে দেখানো হয়েছে:

কোনো CompositingStrategy সেট করা নেই এবং একটি স্বচ্ছ উইন্ডো ব্যাকগ্রাউন্ডযুক্ত অ্যাপের সাথে BlendMode.Clear ব্যবহার করা হচ্ছে। লাল স্ট্যাটাস সার্কেলের চারপাশের অংশের মধ্য দিয়ে গোলাপী ওয়ালপেপারটি দেখা যাচ্ছে।
চিত্র ১৪ : কোনো CompositingStrategy সেট না করে এবং একটি স্বচ্ছ উইন্ডো ব্যাকগ্রাউন্ডযুক্ত অ্যাপে BlendMode.Clear ব্যবহার করা হচ্ছে। লক্ষ্য করুন, কীভাবে লাল স্ট্যাটাস বৃত্তটির চারপাশের অংশের মধ্য দিয়ে গোলাপি ওয়ালপেপারটি দেখা যাচ্ছে।

উল্লেখ্য যে, CompositingStrategy.Offscreen ব্যবহার করার সময়, ড্রয়িং এরিয়ার আকারের একটি অফস্ক্রিন টেক্সচার তৈরি করা হয় এবং স্ক্রিনে রেন্ডার করা হয়। এই স্ট্র্যাটেজি দিয়ে করা যেকোনো ড্রয়িং কমান্ড ডিফল্টরূপে এই অঞ্চলে সীমাবদ্ধ থাকে। নিচের কোড স্নিপেটটি অফস্ক্রিন টেক্সচার ব্যবহারে পরিবর্তনের ফলে সৃষ্ট পার্থক্যগুলো তুলে ধরেছে:

@Composable
fun CompositingStrategyExamples() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize(Alignment.Center)
    ) {
        // Does not clip content even with a graphics layer usage here. By default, graphicsLayer
        // does not allocate + rasterize content into a separate layer but instead is used
        // for isolation. That is draw invalidations made outside of this graphicsLayer will not
        // re-record the drawing instructions in this composable as they have not changed
        Canvas(
            modifier = Modifier
                .graphicsLayer()
                .size(100.dp) // Note size of 100 dp here
                .border(2.dp, color = Color.Blue)
        ) {
            // ... and drawing a size of 200 dp here outside the bounds
            drawRect(color = Color.Magenta, size = Size(200.dp.toPx(), 200.dp.toPx()))
        }

        Spacer(modifier = Modifier.size(300.dp))

        /* Clips content as alpha usage here creates an offscreen buffer to rasterize content
        into first then draws to the original destination */
        Canvas(
            modifier = Modifier
                // force to an offscreen buffer
                .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
                .size(100.dp) // Note size of 100 dp here
                .border(2.dp, color = Color.Blue)
        ) {
            /* ... and drawing a size of 200 dp. However, because of the CompositingStrategy.Offscreen usage above, the
            content gets clipped */
            drawRect(color = Color.Red, size = Size(200.dp.toPx(), 200.dp.toPx()))
        }
    }
}

CompositingStrategy.Auto বনাম CompositingStrategy.Offscreen - offscreen নির্দিষ্ট অঞ্চলে ক্লিপ করে, যেখানে auto তা করে না।
চিত্র ১৫ : CompositingStrategy.Auto বনাম CompositingStrategy.Offscreen - offscreen অঞ্চলটিকে ক্লিপ করে, যেখানে auto তা করে না।
ModulateAlpha

এই কম্পোজিশন স্ট্র্যাটেজিটি graphicsLayer মধ্যে রেকর্ড করা প্রতিটি ড্রয়িং ইনস্ট্রাকশনের জন্য আলফাকে মডিউলেট করে। কোনো RenderEffect সেট করা না থাকলে, এটি 1.0f-এর কম আলফার জন্য কোনো অফস্ক্রিন বাফার তৈরি করবে না, তাই আলফা রেন্ডারিংয়ের জন্য এটি আরও বেশি কার্যকর হতে পারে। তবে, ওভারল্যাপিং কন্টেন্টের ক্ষেত্রে এটি ভিন্ন ফলাফল দিতে পারে। যেসব ক্ষেত্রে আগে থেকেই জানা থাকে যে কন্টেন্ট ওভারল্যাপ করছে না, সেখানে 1-এর কম আলফা মানের জন্য এটি CompositingStrategy.Auto এর চেয়ে ভালো পারফরম্যান্স দিতে পারে।

বিভিন্ন কম্পোজিশন কৌশলের আরেকটি উদাহরণ নিচে দেওয়া হলো - কম্পোজেবলের বিভিন্ন অংশে ভিন্ন ভিন্ন আলফা প্রয়োগ করা, এবং একটি Modulate কৌশল প্রয়োগ করা:

@Preview
@Composable
fun CompositingStrategy_ModulateAlpha() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(32.dp)
    ) {
        // Base drawing, no alpha applied
        Canvas(
            modifier = Modifier.size(200.dp)
        ) {
            drawSquares()
        }

        Spacer(modifier = Modifier.size(36.dp))

        // Alpha 0.5f applied to whole composable
        Canvas(
            modifier = Modifier
                .size(200.dp)
                .graphicsLayer {
                    alpha = 0.5f
                }
        ) {
            drawSquares()
        }
        Spacer(modifier = Modifier.size(36.dp))

        // 0.75f alpha applied to each draw call when using ModulateAlpha
        Canvas(
            modifier = Modifier
                .size(200.dp)
                .graphicsLayer {
                    compositingStrategy = CompositingStrategy.ModulateAlpha
                    alpha = 0.75f
                }
        ) {
            drawSquares()
        }
    }
}

private fun DrawScope.drawSquares() {

    val size = Size(100.dp.toPx(), 100.dp.toPx())
    drawRect(color = Red, size = size)
    drawRect(
        color = Purple, size = size,
        topLeft = Offset(size.width / 4f, size.height / 4f)
    )
    drawRect(
        color = Yellow, size = size,
        topLeft = Offset(size.width / 4f * 2f, size.height / 4f * 2f)
    )
}

val Purple = Color(0xFF7E57C2)
val Yellow = Color(0xFFFFCA28)
val Red = Color(0xFFEF5350)

ModulateAlpha প্রতিটি স্বতন্ত্র ড্র কমান্ডে আলফা সেট প্রয়োগ করে।
চিত্র ১৬ : ModulateAlpha প্রতিটি স্বতন্ত্র ড্র কমান্ডে আলফা সেট প্রয়োগ করে।

একটি কম্পোজেবলের বিষয়বস্তু একটি বিটম্যাপে লিখুন।

একটি সাধারণ ব্যবহার হলো কম্পোজেবল থেকে Bitmap তৈরি করা। আপনার কম্পোজেবলের বিষয়বস্তু একটি Bitmap কপি করতে, rememberGraphicsLayer() ব্যবহার করে একটি GraphicsLayer তৈরি করুন।

drawWithContent() এবং graphicsLayer.record{} ব্যবহার করে ড্রয়িং কমান্ডগুলোকে নতুন লেয়ারে পুনঃনির্দেশিত করুন। তারপর drawLayer ব্যবহার করে দৃশ্যমান ক্যানভাসে লেয়ারটি আঁকুন:

val coroutineScope = rememberCoroutineScope()
val graphicsLayer = rememberGraphicsLayer()
Box(
    modifier = Modifier
        .drawWithContent {
            // call record to capture the content in the graphics layer
            graphicsLayer.record {
                // draw the contents of the composable into the graphics layer
                this@drawWithContent.drawContent()
            }
            // draw the graphics layer on the visible canvas
            drawLayer(graphicsLayer)
        }
        .clickable {
            coroutineScope.launch {
                val bitmap = graphicsLayer.toImageBitmap()
                // do something with the newly acquired bitmap
            }
        }
        .background(Color.White)
) {
    Text("Hello Android", fontSize = 26.sp)
}

আপনি বিটম্যাপটি ডিস্কে সংরক্ষণ করে শেয়ার করতে পারেন। আরও বিস্তারিত জানতে, সম্পূর্ণ উদাহরণ স্নিপেটটি দেখুন। ডিস্কে সংরক্ষণ করার চেষ্টা করার আগে ডিভাইসের অনুমতিগুলো অবশ্যই যাচাই করে নিন।

কাস্টম ড্রয়িং মডিফায়ার

আপনার নিজস্ব কাস্টম মডিফায়ার তৈরি করতে, DrawModifier ইন্টারফেসটি ইমপ্লিমেন্ট করুন। এটি আপনাকে একটি ContentDrawScope এর অ্যাক্সেস দেবে, যা Modifier.drawWithContent() ব্যবহার করার সময় প্রাপ্ত স্কোপের মতোই। এরপর আপনি কোডকে পরিচ্ছন্ন করতে এবং সুবিধাজনক র‍্যাপার সরবরাহ করতে সাধারণ ড্রয়িং অপারেশনগুলোকে কাস্টম ড্রয়িং মডিফায়ারে আলাদা করে নিতে পারেন; উদাহরণস্বরূপ, Modifier.background() একটি সুবিধাজনক DrawModifier

উদাহরণস্বরূপ, আপনি যদি এমন একটি Modifier প্রয়োগ করতে চান যা কন্টেন্টকে উল্লম্বভাবে উল্টে দেয়, তাহলে আপনি নিম্নলিখিতভাবে একটি তৈরি করতে পারেন:

class FlippedModifier : DrawModifier {
    override fun ContentDrawScope.draw() {
        scale(1f, -1f) {
            this@draw.drawContent()
        }
    }
}

fun Modifier.flipped() = this.then(FlippedModifier())

তারপর Text উপর এই ফ্লিপড মডিফায়ারটি প্রয়োগ করুন:

Text(
    "Hello Compose!",
    modifier = Modifier
        .flipped()
)

টেক্সটে কাস্টম ফ্লিপড মডিফায়ার
চিত্র ১৭ : টেক্সটে কাস্টম ফ্লিপড মডিফায়ার

অতিরিক্ত সম্পদ

graphicsLayer এবং কাস্টম ড্রয়িং ব্যবহার করে আরও উদাহরণের জন্য, নিম্নলিখিত রিসোর্সগুলো দেখুন:

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}