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

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
}

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

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 প্রয়োগ করা হয়েছে
অনুবাদ

translationX এবং translationY graphicsLayer দিয়ে পরিবর্তন করা যেতে পারে, 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 কে উল্লম্বভাবে ঘোরাতে এবং rotationZ Z অক্ষের উপর ঘোরাতে সেট করুন (মানক ঘূর্ণন)। এই মানটি ডিগ্রীতে (0-360) নির্দিষ্ট করা হয়েছে।

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

চিত্র ৬ : Modifier.graphicsLayer দ্বারা Image-এ 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 তে সেট করে ঘূর্ণন প্রয়োগ করা হয়েছে

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

শেপ সেই রূপরেখা নির্দিষ্ট করে যেখানে কন্টেন্ট ক্লিপ করা হয় যখন 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))
    )
}

প্রথম বাক্সের বিষয়বস্তু ("হ্যালো কম্পোজ" লেখাটি) বৃত্তের আকারে ক্লিপ করা হয়েছে:

বক্স কম্পোজেবলে ক্লিপ প্রয়োগ করা হয়েছে
চিত্র ৮ : বক্স কম্পোজেবলে ক্লিপ প্রয়োগ করা হয়েছে

যদি আপনি উপরের গোলাপী বৃত্তে একটি 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 নির্ধারণ করে যে কম্পোজেবলের বিষয়বস্তু কীভাবে স্ক্রিনে ইতিমধ্যেই আঁকা অন্যান্য বিষয়বস্তুর সাথে কম্পোজিট (একত্রে রাখা) করা হবে।

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

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

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

অফস্ক্রিন

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

CompositingStrategy.Offscreen ব্যবহারের একটি উদাহরণ হল BlendModes । নিচের উদাহরণটি দেখে নিন, ধরুন আপনি BlendMode.Clear ব্যবহার করে একটি draw কমান্ড জারি করে একটি Image কম্পোজেবল অংশগুলি সরাতে চান। যদি আপনি 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 প্রয়োগের ফলাফল গন্তব্যস্থলের সমস্ত পিক্সেল সাফ করে দেয়, যা আগে থেকেই সেট করা ছিল তা নির্বিশেষে - উইন্ডোর রেন্ডারিং বাফার (কালো) দৃশ্যমান রেখে। alpha জড়িত অনেক BlendModes অফস্ক্রিন বাফার ছাড়া প্রত্যাশা অনুযায়ী কাজ করবে না। লাল বৃত্ত নির্দেশকের চারপাশে কালো রিংটি লক্ষ্য করুন:

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

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

কোনও কম্পোজিটিংস্ট্র্যাটেজি সেট করা নেই এবং BlendMode.Clear ব্যবহার করা হচ্ছে এমন একটি অ্যাপ দিয়ে যার উইন্ডো ব্যাকগ্রাউন্ড স্বচ্ছ। গোলাপী ওয়ালপেপারটি লাল স্ট্যাটাস সার্কেলের চারপাশের এলাকা দিয়ে দেখানো হয়েছে।
চিত্র ১৪ : কোনও কম্পোজিটিংস্ট্র্যাটেজি সেট করা নেই এবং 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()))
        }
    }
}

কম্পোজিটিংস্ট্র্যাটেজি.অটো বনাম কম্পোজিটিংস্ট্র্যাটেজি.অফস্ক্রিন - অফস্ক্রিন ক্লিপগুলি সেই অঞ্চলে, যেখানে অটো করে না
চিত্র ১৫ : কম্পোজিটিংস্ট্র্যাটেজি.অটো বনাম কম্পোজিটিংস্ট্র্যাটেজি.অফস্ক্রিন - অফস্ক্রিন ক্লিপগুলি সেই অঞ্চলে, যেখানে অটো করে না
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 এবং কাস্টম অঙ্কন ব্যবহারের আরও উদাহরণের জন্য, নিম্নলিখিত সংস্থানগুলি দেখুন:

{% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %} {% অক্ষরে অক্ষরে %} {% এন্ডভারব্যাটিম %}