Skip to content

Canvas & Metal/OpenGL ES vs. Native UI

Canvas Tabanlı Özel Çizim

Android Canvas API

Android Canvas sistemi, 2D grafik çizimi için güçlü ve esnek bir framework sağlar. Donanım hızlandırma ile birlikte yüksek performanslı özel UI bileşenleri oluşturmaya olanak tanır.

Advanced Canvas Techniques

kotlin
class AdvancedCanvasView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val path = Path()
    private val matrix = Matrix()
    private val gradientShader: LinearGradient
    private val shadowPaint = Paint()
    
    // Animation properties
    private var animationProgress = 0f
    private val valueAnimator = ValueAnimator.ofFloat(0f, 1f)
    
    init {
        // Gradient shader oluştur
        gradientShader = LinearGradient(
            0f, 0f, 0f, 200f,
            intArrayOf(Color.CYAN, Color.BLUE, Color.MAGENTA),
            floatArrayOf(0f, 0.5f, 1f),
            Shader.TileMode.CLAMP
        )
        
        // Shadow paint ayarla
        shadowPaint.apply {
            color = Color.BLACK
            alpha = 100
            maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL)
        }
        
        setupAnimator()
    }
    
    private fun setupAnimator() {
        valueAnimator.apply {
            duration = 2000
            repeatCount = ValueAnimator.INFINITE
            repeatMode = ValueAnimator.REVERSE
            interpolator = AccelerateDecelerateInterpolator()
            
            addUpdateListener { animator ->
                animationProgress = animator.animatedValue as Float
                invalidate()
            }
        }
        valueAnimator.start()
    }
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        val centerX = width / 2f
        val centerY = height / 2f
        val radius = (Math.min(width, height) / 3f) * (0.8f + 0.2f * animationProgress)
        
        // Background gradient
        drawBackgroundGradient(canvas)
        
        // Shadow layer
        drawShadowLayer(canvas, centerX, centerY + 10f, radius)
        
        // Main shape with complex path
        drawMainShape(canvas, centerX, centerY, radius)
        
        // Overlay effects
        drawOverlayEffects(canvas, centerX, centerY, radius)
    }
    
    private fun drawBackgroundGradient(canvas: Canvas) {
        paint.shader = gradientShader
        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
        paint.shader = null
    }
    
    private fun drawShadowLayer(canvas: Canvas, x: Float, y: Float, radius: Float) {
        canvas.drawCircle(x, y, radius, shadowPaint)
    }
    
    private fun drawMainShape(canvas: Canvas, centerX: Float, centerY: Float, radius: Float) {
        path.reset()
        
        // Karmaşık path oluştur (yıldız benzeri şekil)
        val points = 8
        val outerRadius = radius
        val innerRadius = radius * 0.6f
        
        for (i in 0 until points * 2) {
            val angle = (i * Math.PI / points).toFloat()
            val currentRadius = if (i % 2 == 0) outerRadius else innerRadius
            val x = centerX + cos(angle) * currentRadius
            val y = centerY + sin(angle) * currentRadius
            
            if (i == 0) {
                path.moveTo(x, y)
            } else {
                path.lineTo(x, y)
            }
        }
        path.close()
        
        // Matrix transformations
        matrix.reset()
        matrix.postRotate(animationProgress * 360f, centerX, centerY)
        matrix.postScale(
            1f + animationProgress * 0.1f,
            1f + animationProgress * 0.1f,
            centerX,
            centerY
        )
        path.transform(matrix)
        
        // Fill with gradient
        paint.apply {
            style = Paint.Style.FILL
            color = Color.WHITE
            alpha = (255 * (0.8f + 0.2f * animationProgress)).toInt()
        }
        canvas.drawPath(path, paint)
        
        // Stroke
        paint.apply {
            style = Paint.Style.STROKE
            strokeWidth = 4f
            color = Color.BLACK
            alpha = 255
        }
        canvas.drawPath(path, paint)
    }
    
    private fun drawOverlayEffects(canvas: Canvas, centerX: Float, centerY: Float, radius: Float) {
        // Particle effect benzeri circles
        paint.style = Paint.Style.FILL
        for (i in 0 until 5) {
            val angle = (animationProgress * 360f + i * 72f) * Math.PI / 180f
            val distance = radius * 1.5f
            val x = centerX + cos(angle) * distance
            val y = centerY + sin(angle) * distance
            val particleRadius = 8f * (1f - animationProgress * 0.3f)
            
            paint.apply {
                color = Color.YELLOW
                alpha = (255 * (1f - animationProgress)).toInt()
            }
            canvas.drawCircle(x.toFloat(), y.toFloat(), particleRadius, paint)
        }
    }
    
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        valueAnimator.cancel()
    }
}

Performance-Optimized Canvas Drawing

kotlin
class OptimizedCanvasRenderer {
    private val pathCache = mutableMapOf<String, Path>()
    private val paintCache = mutableMapOf<String, Paint>()
    private val bitmapCache = LruCache<String, Bitmap>(20)
    
    fun drawOptimizedChart(
        canvas: Canvas,
        dataPoints: List<PointF>,
        bounds: RectF
    ) {
        // Path caching
        val pathKey = "chart_${dataPoints.hashCode()}"
        val path = pathCache.getOrPut(pathKey) {
            createChartPath(dataPoints, bounds)
        }
        
        // Paint caching
        val linePaint = paintCache.getOrPut("line_paint") {
            Paint(Paint.ANTI_ALIAS_FLAG).apply {
                style = Paint.Style.STROKE
                strokeWidth = 4f
                color = Color.BLUE
                strokeCap = Paint.Cap.ROUND
                strokeJoin = Paint.Join.ROUND
            }
        }
        
        val fillPaint = paintCache.getOrPut("fill_paint") {
            Paint(Paint.ANTI_ALIAS_FLAG).apply {
                style = Paint.Style.FILL
                shader = LinearGradient(
                    bounds.left, bounds.top,
                    bounds.left, bounds.bottom,
                    Color.BLUE, Color.TRANSPARENT,
                    Shader.TileMode.CLAMP
                )
                alpha = 100
            }
        }
        
        // Efficient drawing
        canvas.drawPath(path, fillPaint)
        canvas.drawPath(path, linePaint)
    }
    
    private fun createChartPath(dataPoints: List<PointF>, bounds: RectF): Path {
        val path = Path()
        
        if (dataPoints.isEmpty()) return path
        
        // Move to first point
        path.moveTo(dataPoints[0].x, dataPoints[0].y)
        
        // Smooth curve through points (Catmull-Rom spline)
        for (i in 1 until dataPoints.size - 1) {
            val p0 = if (i > 0) dataPoints[i - 1] else dataPoints[i]
            val p1 = dataPoints[i]
            val p2 = dataPoints[i + 1]
            val p3 = if (i < dataPoints.size - 2) dataPoints[i + 2] else dataPoints[i + 1]
            
            // Control points for smooth curve
            val cp1x = p1.x + (p2.x - p0.x) / 6f
            val cp1y = p1.y + (p2.y - p0.y) / 6f
            val cp2x = p2.x - (p3.x - p1.x) / 6f
            val cp2y = p2.y - (p3.y - p1.y) / 6f
            
            path.cubicTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y)
        }
        
        return path
    }
}

iOS Core Graphics ve Custom Drawing

Advanced Core Graphics Implementation

swift
class AdvancedDrawingView: UIView {
    private var animationProgress: CGFloat = 0
    private var displayLink: CADisplayLink?
    private let particleSystem = ParticleSystem()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupAnimation()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupAnimation()
    }
    
    private func setupAnimation() {
        displayLink = CADisplayLink(target: self, selector: #selector(updateAnimation))
        displayLink?.add(to: .main, forMode: .common)
    }
    
    @objc private func updateAnimation() {
        animationProgress += 0.016 // ~60 FPS
        if animationProgress > 1.0 {
            animationProgress = 0
        }
        
        particleSystem.update(deltaTime: 0.016)
        setNeedsDisplay()
    }
    
    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        
        // Background gradient
        drawBackgroundGradient(in: context, rect: rect)
        
        // Complex shapes with transforms
        drawAnimatedShapes(in: context, rect: rect)
        
        // Particle system
        particleSystem.render(in: context)
        
        // Text with custom effects
        drawStyledText(in: context, rect: rect)
    }
    
    private func drawBackgroundGradient(in context: CGContext, rect: CGRect) {
        let colors = [UIColor.blue.cgColor, UIColor.purple.cgColor, UIColor.black.cgColor]
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: [0, 0.5, 1])!
        
        context.drawLinearGradient(
            gradient,
            start: CGPoint.zero,
            end: CGPoint(x: rect.width, y: rect.height),
            options: []
        )
    }
    
    private func drawAnimatedShapes(in context: CGContext, rect: CGRect) {
        context.saveGState()
        
        let centerX = rect.width / 2
        let centerY = rect.height / 2
        
        // Apply transforms
        context.translateBy(x: centerX, y: centerY)
        context.rotate(by: animationProgress * 2 * .pi)
        context.scaleBy(x: 1 + animationProgress * 0.2, y: 1 + animationProgress * 0.2)
        
        // Draw complex path
        let path = CGMutablePath()
        let radius: CGFloat = 60
        let points = 6
        
        for i in 0..<points {
            let angle = CGFloat(i) * 2 * .pi / CGFloat(points)
            let x = cos(angle) * radius
            let y = sin(angle) * radius
            
            if i == 0 {
                path.move(to: CGPoint(x: x, y: y))
            } else {
                path.addLine(to: CGPoint(x: x, y: y))
            }
        }
        path.closeSubpath()
        
        // Fill with pattern
        context.setFillColor(UIColor.white.withAlphaComponent(0.8).cgColor)
        context.addPath(path)
        context.fillPath()
        
        // Stroke with shadow
        context.setShadow(offset: CGSize(width: 2, height: 2), blur: 4)
        context.setStrokeColor(UIColor.yellow.cgColor)
        context.setLineWidth(3)
        context.addPath(path)
        context.strokePath()
        
        context.restoreGState()
    }
    
    private func drawStyledText(in context: CGContext, rect: CGRect) {
        let text = "Advanced Graphics"
        let font = UIFont.boldSystemFont(ofSize: 24)
        let attributes: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: UIColor.white,
            .strokeColor: UIColor.black,
            .strokeWidth: -2.0
        ]
        
        let attributedString = NSAttributedString(string: text, attributes: attributes)
        let textSize = attributedString.size()
        let textRect = CGRect(
            x: (rect.width - textSize.width) / 2,
            y: rect.height - textSize.height - 20,
            width: textSize.width,
            height: textSize.height
        )
        
        attributedString.draw(in: textRect)
    }
    
    deinit {
        displayLink?.invalidate()
    }
}

// Particle System for advanced effects
class ParticleSystem {
    private var particles: [Particle] = []
    private let maxParticles = 50
    
    init() {
        for _ in 0..<maxParticles {
            particles.append(Particle())
        }
    }
    
    func update(deltaTime: TimeInterval) {
        for particle in particles {
            particle.update(deltaTime: deltaTime)
        }
    }
    
    func render(in context: CGContext) {
        for particle in particles {
            particle.render(in: context)
        }
    }
}

class Particle {
    var position: CGPoint
    var velocity: CGPoint
    var life: CGFloat
    var maxLife: CGFloat
    var color: UIColor
    
    init() {
        position = CGPoint(x: CGFloat.random(in: 0...400), y: CGFloat.random(in: 0...800))
        velocity = CGPoint(x: CGFloat.random(in: -50...50), y: CGFloat.random(in: -100...0))
        life = 1.0
        maxLife = 1.0
        color = UIColor(hue: CGFloat.random(in: 0...1), saturation: 1, brightness: 1, alpha: 1)
    }
    
    func update(deltaTime: TimeInterval) {
        position.x += velocity.x * CGFloat(deltaTime)
        position.y += velocity.y * CGFloat(deltaTime)
        life -= CGFloat(deltaTime)
        
        if life <= 0 {
            // Reset particle
            position = CGPoint(x: CGFloat.random(in: 0...400), y: 800)
            life = maxLife
        }
    }
    
    func render(in context: CGContext) {
        let alpha = life / maxLife
        context.setFillColor(color.withAlphaComponent(alpha).cgColor)
        context.fillEllipse(in: CGRect(x: position.x - 2, y: position.y - 2, width: 4, height: 4))
    }
}

Metal ve OpenGL ES ile Düşük Seviyeli Grafikler

Metal Shaders ve Gelişmiş Render İşlemleri

swift
import Metal
import MetalKit

class MetalAdvancedRenderer: NSObject, MTKViewDelegate {
    private var device: MTLDevice
    private var commandQueue: MTLCommandQueue
    private var renderPipelineState: MTLRenderPipelineState
    private var computePipelineState: MTLComputePipelineState
    private var vertexBuffer: MTLBuffer
    private var uniformBuffer: MTLBuffer
    
    struct Uniforms {
        var time: Float
        var resolution: simd_float2
        var modelViewProjectionMatrix: simd_float4x4
    }
    
    init?(metalKitView: MTKView) {
        guard let device = MTLCreateSystemDefaultDevice(),
              let commandQueue = device.makeCommandQueue() else {
            return nil
        }
        
        self.device = device
        self.commandQueue = commandQueue
        
        // Vertex data
        let vertices: [Float] = [
            -1.0, -1.0, 0.0, 1.0,  // Bottom-left
             1.0, -1.0, 0.0, 1.0,  // Bottom-right
             1.0,  1.0, 0.0, 1.0,  // Top-right
            -1.0,  1.0, 0.0, 1.0   // Top-left
        ]
        
        vertexBuffer = device.makeBuffer(bytes: vertices, length: vertices.count * MemoryLayout<Float>.size, options: [])!
        uniformBuffer = device.makeBuffer(length: MemoryLayout<Uniforms>.size, options: [])!
        
        super.init()
        
        setupPipelines(metalKitView: metalKitView)
        
        metalKitView.device = device
        metalKitView.delegate = self
        metalKitView.colorPixelFormat = .bgra8Unorm
    }
    
    private func setupPipelines(metalKitView: MTKView) {
        guard let library = device.makeDefaultLibrary() else {
            fatalError("Could not load default library")
        }
        
        // Render pipeline
        let vertexFunction = library.makeFunction(name: "vertexShader")
        let fragmentFunction = library.makeFunction(name: "fragmentShader")
        
        let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
        renderPipelineDescriptor.vertexFunction = vertexFunction
        renderPipelineDescriptor.fragmentFunction = fragmentFunction
        renderPipelineDescriptor.colorAttachments[0].pixelFormat = metalKitView.colorPixelFormat
        
        do {
            renderPipelineState = try device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
        } catch {
            fatalError("Could not create render pipeline state: \(error)")
        }
        
        // Compute pipeline
        let computeFunction = library.makeFunction(name: "postProcessCompute")!
        do {
            computePipelineState = try device.makeComputePipelineState(function: computeFunction)
        } catch {
            fatalError("Could not create compute pipeline state: \(error)")
        }
    }
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        // Handle size changes
    }
    
    func draw(in view: MTKView) {
        guard let commandBuffer = commandQueue.makeCommandBuffer(),
              let renderPassDescriptor = view.currentRenderPassDescriptor,
              let drawable = view.currentDrawable else {
            return
        }
        
        // Update uniforms
        let uniforms = Uniforms(
            time: Float(CACurrentMediaTime()),
            resolution: simd_float2(Float(view.bounds.width), Float(view.bounds.height)),
            modelViewProjectionMatrix: matrix_identity_float4x4
        )
        
        let uniformBufferPointer = uniformBuffer.contents().bindMemory(to: Uniforms.self, capacity: 1)
        uniformBufferPointer.pointee = uniforms
        
        // Render pass
        let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
        renderEncoder.setRenderPipelineState(renderPipelineState)
        renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
        renderEncoder.setVertexBuffer(uniformBuffer, offset: 0, index: 1)
        renderEncoder.setFragmentBuffer(uniformBuffer, offset: 0, index: 0)
        renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
        renderEncoder.endEncoding()
        
        commandBuffer.present(drawable)
        commandBuffer.commit()
    }
}

Metal Shaders (MSL)

metal
// Vertex Shader
#include <metal_stdlib>
using namespace metal;

struct Vertex {
    float4 position [[attribute(0)]];
};

struct Uniforms {
    float time;
    float2 resolution;
    float4x4 modelViewProjectionMatrix;
};

struct VertexOut {
    float4 position [[position]];
    float2 uv;
};

vertex VertexOut vertexShader(
    const device Vertex* vertices [[buffer(0)]],
    const device Uniforms& uniforms [[buffer(1)]],
    uint vid [[vertex_id]]
) {
    VertexOut out;
    out.position = uniforms.modelViewProjectionMatrix * vertices[vid].position;
    out.uv = (vertices[vid].position.xy + 1.0) * 0.5;
    return out;
}

// Fragment Shader with procedural graphics
fragment float4 fragmentShader(
    VertexOut in [[stage_in]],
    const device Uniforms& uniforms [[buffer(0)]]
) {
    float2 uv = in.uv;
    float2 center = float2(0.5);
    float2 pos = uv - center;
    
    // Animated parameters
    float time = uniforms.time;
    float aspect = uniforms.resolution.x / uniforms.resolution.y;
    pos.x *= aspect;
    
    // Distance field calculations
    float distance = length(pos);
    float angle = atan2(pos.y, pos.x);
    
    // Animated pattern
    float pattern = sin(distance * 20.0 - time * 2.0) * 0.5 + 0.5;
    float spiral = sin(angle * 5.0 + distance * 10.0 - time) * 0.5 + 0.5;
    
    // Color mixing
    float3 color1 = float3(0.2, 0.7, 1.0);
    float3 color2 = float3(1.0, 0.3, 0.8);
    float3 finalColor = mix(color1, color2, pattern * spiral);
    
    // Vignette effect
    float vignette = 1.0 - smoothstep(0.3, 0.8, distance);
    finalColor *= vignette;
    
    return float4(finalColor, 1.0);
}

// Compute Shader for post-processing
kernel void postProcessCompute(
    texture2d<float, access::read> inputTexture [[texture(0)]],
    texture2d<float, access::write> outputTexture [[texture(1)]],
    const device Uniforms& uniforms [[buffer(0)]],
    uint2 gid [[thread_position_in_grid]]
) {
    if (gid.x >= outputTexture.get_width() || gid.y >= outputTexture.get_height()) {
        return;
    }
    
    float2 textureSize = float2(inputTexture.get_width(), inputTexture.get_height());
    float2 uv = float2(gid) / textureSize;
    
    // Sample neighboring pixels for blur effect
    float4 color = float4(0.0);
    int kernelSize = 3;
    float weight = 1.0 / float(kernelSize * kernelSize);
    
    for (int x = -kernelSize/2; x <= kernelSize/2; x++) {
        for (int y = -kernelSize/2; y <= kernelSize/2; y++) {
            uint2 sampleCoord = uint2(
                clamp(int(gid.x) + x, 0, int(textureSize.x) - 1),
                clamp(int(gid.y) + y, 0, int(textureSize.y) - 1)
            );
            color += inputTexture.read(sampleCoord) * weight;
        }
    }
    
    // Apply color grading
    color.rgb = pow(color.rgb, float3(1.2));
    color.rgb = saturate(color.rgb * 1.1);
    
    outputTexture.write(color, gid);
}

Native UI vs Özel Grafikler Performansı

Native UI vs Özel Grafikler Karşılaştırması

Performans Özellikleri

Performans Metrikleri Karşılaştırması:
┌─────────────────────┬─────────────┬──────────────┬─────────────┐
│ Yön                 │ Native UI   │ Canvas API   │ Metal/GL    │
├─────────────────────┼─────────────┼──────────────┼─────────────┤
│ Kurulum Yükü        │ Düşük       │ Orta         │ Yüksek      │
│ Render Hızı         │ Hızlı       │ Orta         │ Çok Hızlı   │
│ Bellek Kullanımı    │ Optimize    │ Yüksek       │ Değişken    │
│ Esneklik            │ Sınırlı     │ Yüksek       │ Çok Yüksek  │
│ Platform Entegrasyon│ Mükemmel    │ İyi          │ Sınırlı     │
│ Öğrenme Eğrisi      │ Kolay       │ Orta         │ Zor         │
└─────────────────────┴─────────────┴──────────────┴─────────────┘

Android Canvas API Gelişmiş Teknikler

Donanım Hızlandırmalı Özel ViewGroup

kotlin
class PerformanceCanvasView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.FILL
        color = Color.BLUE
    }
    
    private val path = Path()
    private val matrix = Matrix()
    private val rect = RectF()
    
    // Hardware acceleration optimization
    private var bitmap: Bitmap? = null
    private var bitmapCanvas: Canvas? = null
    private var isDirty = true
    
    init {
        // Enable hardware acceleration
        setLayerType(LAYER_TYPE_HARDWARE, null)
        
        // Optimize drawing
        setWillNotDraw(false)
    }
    
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        
        // Create offscreen bitmap for complex drawings
        bitmap?.recycle()
        bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        bitmapCanvas = Canvas(bitmap!!)
        isDirty = true
    }
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        // Only redraw if necessary
        if (isDirty) {
            drawComplexGraphics(bitmapCanvas!!)
            isDirty = false
        }
        
        // Draw cached bitmap
        bitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
        
        // Draw dynamic elements directly
        drawDynamicElements(canvas)
    }
    
    private fun drawComplexGraphics(canvas: Canvas) {
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
        
        // Complex path drawing
        path.reset()
        path.moveTo(50f, 50f)
        path.quadTo(100f, 25f, 150f, 50f)
        path.quadTo(200f, 75f, 250f, 50f)
        
        // Gradient shader
        val gradient = LinearGradient(
            0f, 0f, width.toFloat(), height.toFloat(),
            intArrayOf(Color.RED, Color.GREEN, Color.BLUE),
            null,
            Shader.TileMode.CLAMP
        )
        paint.shader = gradient
        
        canvas.drawPath(path, paint)
        
        // Reset shader
        paint.shader = null
    }
    
    private fun drawDynamicElements(canvas: Canvas) {
        // Draw elements that change frequently
        val time = System.currentTimeMillis()
        val x = (Math.sin(time * 0.001) * 100 + width / 2).toFloat()
        val y = (Math.cos(time * 0.001) * 100 + height / 2).toFloat()
        
        paint.color = Color.YELLOW
        canvas.drawCircle(x, y, 20f, paint)
    }
    
    fun invalidateDrawing() {
        isDirty = true
        invalidate()
    }
    
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        bitmap?.recycle()
        bitmap = null
        bitmapCanvas = null
    }
}

Advanced Canvas Transformations

kotlin
class TransformationCanvas : View {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val matrix = Matrix()
    private val camera = Camera()
    
    private var rotationX = 0f
    private var rotationY = 0f
    private var rotationZ = 0f
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        val centerX = width / 2f
        val centerY = height / 2f
        
        // 3D transformation using Camera
        camera.save()
        camera.rotateX(rotationX)
        camera.rotateY(rotationY)
        camera.rotateZ(rotationZ)
        camera.getMatrix(matrix)
        camera.restore()
        
        // Adjust matrix for center rotation
        matrix.preTranslate(-centerX, -centerY)
        matrix.postTranslate(centerX, centerY)
        
        canvas.save()
        canvas.concat(matrix)
        
        // Draw 3D-transformed content
        paint.color = Color.BLUE
        canvas.drawRect(
            centerX - 100f, centerY - 100f,
            centerX + 100f, centerY + 100f,
            paint
        )
        
        canvas.restore()
        
        // Advanced clipping
        drawClippedGraphics(canvas)
    }
    
    private fun drawClippedGraphics(canvas: Canvas) {
        canvas.save()
        
        // Create complex clipping path
        val clipPath = Path().apply {
            addCircle(width / 4f, height / 4f, 100f, Path.Direction.CW)
            addRect(
                width * 3f / 4f - 50f, height / 4f - 50f,
                width * 3f / 4f + 50f, height / 4f + 50f,
                Path.Direction.CW
            )
        }
        
        canvas.clipPath(clipPath)
        
        // Draw gradient background
        val gradient = RadialGradient(
            width / 2f, height / 2f, width / 2f,
            intArrayOf(Color.MAGENTA, Color.CYAN, Color.YELLOW),
            floatArrayOf(0f, 0.5f, 1f),
            Shader.TileMode.CLAMP
        )
        
        paint.shader = gradient
        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
        paint.shader = null
        
        canvas.restore()
    }
    
    fun setRotation(x: Float, y: Float, z: Float) {
        rotationX = x
        rotationY = y
        rotationZ = z
        invalidate()
    }
}

iOS Core Graphics Gelişmiş Uygulaması

Özel Core Graphics Çizimi

swift
import UIKit
import CoreGraphics
import QuartzCore

class AdvancedCoreGraphicsView: UIView {
    private var cachedLayer: CALayer?
    private var needsRedraw = true
    
    override class var layerClass: AnyClass {
        return CALayer.self
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        setupOptimizations()
    }
    
    private func setupOptimizations() {
        // Enable rasterization for complex drawings
        layer.shouldRasterize = true
        layer.rasterizationScale = UIScreen.main.scale
        
        // Set drawing delegate
        layer.delegate = self
        layer.setNeedsDisplay()
    }
    
    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        
        if needsRedraw {
            drawComplexGraphics(in: context, rect: rect)
            needsRedraw = false
        }
        
        drawDynamicContent(in: context, rect: rect)
    }
    
    private func drawComplexGraphics(in context: CGContext, rect: CGRect) {
        // Create gradient
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let colors = [UIColor.red.cgColor, UIColor.blue.cgColor, UIColor.green.cgColor]
        let locations: [CGFloat] = [0.0, 0.5, 1.0]
        
        guard let gradient = CGGradient(colorsSpace: colorSpace,
                                      colors: colors as CFArray,
                                      locations: locations) else { return }
        
        // Draw radial gradient
        context.drawRadialGradient(
            gradient,
            startCenter: CGPoint(x: rect.midX, y: rect.midY),
            startRadius: 0,
            endCenter: CGPoint(x: rect.midX, y: rect.midY),
            endRadius: min(rect.width, rect.height) / 2,
            options: []
        )
        
        // Complex path with Bezier curves
        let path = CGMutablePath()
        path.move(to: CGPoint(x: 50, y: 50))
        path.addQuadCurve(
            to: CGPoint(x: 250, y: 50),
            control: CGPoint(x: 150, y: 20)
        )
        path.addQuadCurve(
            to: CGPoint(x: 250, y: 250),
            control: CGPoint(x: 280, y: 150)
        )
        path.addQuadCurve(
            to: CGPoint(x: 50, y: 250),
            control: CGPoint(x: 150, y: 280)
        )
        path.addQuadCurve(
            to: CGPoint(x: 50, y: 50),
            control: CGPoint(x: 20, y: 150)
        )
        
        context.setFillColor(UIColor.yellow.withAlphaComponent(0.7).cgColor)
        context.addPath(path)
        context.fillPath()
    }
    
    private func drawDynamicContent(in context: CGContext, rect: CGRect) {
        // Animated circle
        let time = CACurrentMediaTime()
        let x = sin(time) * 50 + rect.midX
        let y = cos(time) * 50 + rect.midY
        
        context.setFillColor(UIColor.orange.cgColor)
        context.fillEllipse(in: CGRect(x: x - 15, y: y - 15, width: 30, height: 30))
        
        // Text rendering with custom attributes
        drawCustomText(in: context, at: CGPoint(x: rect.midX, y: rect.maxY - 50))
    }
    
    private func drawCustomText(in context: CGContext, at point: CGPoint) {
        let text = "Advanced Core Graphics"
        let font = CTFontCreateWithName("Helvetica-Bold" as CFString, 20, nil)
        let attributes: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: UIColor.white,
            .strokeColor: UIColor.black,
            .strokeWidth: -2.0
        ]
        
        let attributedString = NSAttributedString(string: text, attributes: attributes)
        let line = CTLineCreateWithAttributedString(attributedString)
        
        context.saveGState()
        context.textPosition = point
        CTLineDraw(line, context)
        context.restoreGState()
    }
    
    func invalidateDrawing() {
        needsRedraw = true
        setNeedsDisplay()
    }
}

// MARK: - CALayerDelegate
extension AdvancedCoreGraphicsView: CALayerDelegate {
    func draw(_ layer: CALayer, in ctx: CGContext) {
        // Custom layer drawing
        ctx.setFillColor(UIColor.blue.withAlphaComponent(0.3).cgColor)
        ctx.fill(layer.bounds)
    }
}

Metal Integration with UIKit

swift
import MetalKit
import Metal

class MetalUIKitView: UIView {
    private var metalView: MTKView!
    private var device: MTLDevice!
    private var commandQueue: MTLCommandQueue!
    private var pipelineState: MTLRenderPipelineState!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        setupMetal()
    }
    
    private func setupMetal() {
        guard let device = MTLCreateSystemDefaultDevice() else {
            fatalError("Metal not supported")
        }
        
        self.device = device
        self.commandQueue = device.makeCommandQueue()
        
        // Create MTKView
        metalView = MTKView(frame: bounds, device: device)
        metalView.delegate = self
        metalView.preferredFramesPerSecond = 60
        metalView.colorPixelFormat = .bgra8Unorm_srgb
        addSubview(metalView)
        
        // Setup render pipeline
        setupRenderPipeline()
    }
    
    private func setupRenderPipeline() {
        guard let library = device.makeDefaultLibrary() else {
            fatalError("Could not load default library")
        }
        
        let vertexFunction = library.makeFunction(name: "vertex_main")
        let fragmentFunction = library.makeFunction(name: "fragment_main")
        
        let pipelineDescriptor = MTLRenderPipelineDescriptor()
        pipelineDescriptor.vertexFunction = vertexFunction
        pipelineDescriptor.fragmentFunction = fragmentFunction
        pipelineDescriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat
        
        do {
            pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
        } catch {
            fatalError("Could not create render pipeline state: \(error)")
        }
    }
}

// MARK: - MTKViewDelegate
extension MetalUIKitView: MTKViewDelegate {
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        // Handle size changes
    }
    
    func draw(in view: MTKView) {
        guard let drawable = view.currentDrawable,
              let renderPassDescriptor = view.currentRenderPassDescriptor,
              let commandBuffer = commandQueue.makeCommandBuffer(),
              let renderEncoder = commandBuffer.makeRenderCommandEncoder(
                descriptor: renderPassDescriptor
              ) else {
            return
        }
        
        // Clear background
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
            red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0
        )
        
        // Set render pipeline
        renderEncoder.setRenderPipelineState(pipelineState)
        
        // Draw triangle
        let vertices: [Float] = [
             0.0,  0.5, 0.0, 1.0, 0.0, 0.0, 1.0,  // Top vertex (red)
            -0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0,  // Bottom left (green)
             0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 1.0   // Bottom right (blue)
        ]
        
        renderEncoder.setVertexBytes(vertices, length: vertices.count * MemoryLayout<Float>.size, index: 0)
        renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
        
        renderEncoder.endEncoding()
        commandBuffer.present(drawable)
        commandBuffer.commit()
    }
}

Performans Ölçümü ve Analizi

Canvas vs Native UI Karşılaştırması

kotlin
class PerformanceBenchmark {
    private val renderTimes = mutableListOf<Long>()
    private val maxSamples = 100
    
    fun benchmarkCanvasDrawing(canvas: Canvas, iterations: Int): BenchmarkResult {
        renderTimes.clear()
        
        repeat(iterations) {
            val startTime = System.nanoTime()
            
            // Canvas drawing operations
            drawComplexCanvas(canvas)
            
            val endTime = System.nanoTime()
            renderTimes.add(endTime - startTime)
        }
        
        return calculateBenchmarkResult("Canvas Drawing")
    }
    
    fun benchmarkNativeUI(viewGroup: ViewGroup, iterations: Int): BenchmarkResult {
        renderTimes.clear()
        
        repeat(iterations) {
            val startTime = System.nanoTime()
            
            // Native UI operations
            updateNativeViews(viewGroup)
            
            val endTime = System.nanoTime()
            renderTimes.add(endTime - startTime)
        }
        
        return calculateBenchmarkResult("Native UI")
    }
    
    private fun drawComplexCanvas(canvas: Canvas) {
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        
        // Draw 100 circles with gradients
        repeat(100) { i ->
            val gradient = RadialGradient(
                (i * 10).toFloat(), (i * 5).toFloat(), 50f,
                Color.RED, Color.BLUE, Shader.TileMode.CLAMP
            )
            paint.shader = gradient
            canvas.drawCircle((i * 10).toFloat(), (i * 5).toFloat(), 20f, paint)
        }
        
        paint.shader = null
    }
    
    private fun updateNativeViews(viewGroup: ViewGroup) {
        // Update 100 native views
        repeat(100) { i ->
            val view = viewGroup.getChildAt(i % viewGroup.childCount)
            view?.apply {
                x = (i * 10).toFloat()
                y = (i * 5).toFloat()
                scaleX = 1.0f + (i % 10) * 0.1f
                scaleY = 1.0f + (i % 10) * 0.1f
            }
        }
    }
    
    private fun calculateBenchmarkResult(name: String): BenchmarkResult {
        val avgTime = renderTimes.average()
        val minTime = renderTimes.minOrNull() ?: 0L
        val maxTime = renderTimes.maxOrNull() ?: 0L
        val fps = 1_000_000_000.0 / avgTime // Convert nanoseconds to FPS
        
        return BenchmarkResult(
            name = name,
            averageTimeNs = avgTime.toLong(),
            minTimeNs = minTime,
            maxTimeNs = maxTime,
            estimatedFPS = fps
        )
    }
    
    data class BenchmarkResult(
        val name: String,
        val averageTimeNs: Long,
        val minTimeNs: Long,
        val maxTimeNs: Long,
        val estimatedFPS: Double
    ) {
        override fun toString(): String {
            return """
                $name Benchmark Results:
                Average Time: ${averageTimeNs / 1_000_000.0} ms
                Min Time: ${minTimeNs / 1_000_000.0} ms
                Max Time: ${maxTimeNs / 1_000_000.0} ms
                Estimated FPS: ${String.format("%.1f", estimatedFPS)}
            """.trimIndent()
        }
    }
}

En İyi Uygulamalar Özeti

Canvas API Ne Zaman Kullanılmalı

kotlin
/*
✅ Canvas şu durumlarda kullanılmalı:
- Karmaşık özel çizimler
- Parçacık sistemleri
- Oyunlar ve animasyonlar
- Veri görselleştirmeleri
- Özel şekiller ve yollar
- Performans kritik grafikler

❌ Canvas şu durumlarda kullanılmamalı:
- Basit UI düzenleri
- Standart UI bileşenleri
- Metin ağırlıklı arayüzler
- Formlar ve girişler
- Liste/ızgara düzenleri
*/

Native UI Ne Zaman Kullanılmalı

kotlin
/*
✅ Native UI şu durumlarda kullanılmalı:
- Standart UI desenleri
- Platform tutarlılığı gerektiğinde
- Erişilebilirlik gereksinimleri
- Form tabanlı arayüzler
- Hızlı geliştirme gerektiğinde
- Platform animasyonları

❌ Native UI şu durumlarda kullanılmamalı:
- Karmaşık özel grafikler
- Oyun benzeri arayüzler
- Ağır animasyon gereksinimleri
- Piksel hassasiyeti gerektiğinde
- Performans kritik render işlemleri
*/

Eren Demir tarafından oluşturulmuştur.