Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Build your own WebP codec in Swift

Build your own WebP codec in Swift

Avatar for Kishikawa Katsumi

Kishikawa Katsumi

September 20, 2025
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. r 8FC1ͬͯͳʹʁ r ̎छྨͷѹॖϞʔυ r J04Ͱ͸Ͳ͜·Ͱ࢖͑Δʁ r σʔλߏ଄ʹ͍ͭͯ r ϔομΛಡΉ

    r খ͞ͳ࣮૷Ͱʮ8FC1ը૾ʯΛ࡞Δ r ·ͱΊͱ࣍ͷεςοϓ 0VUMJOF ໨࣍
  2. r 8FCαΠτͰར༻͢Δը૾ͷͨΊʹ࡞ΒΕͨը૾ѹॖܗࣜ r ಉ͡ݟͨ໨ͰϑΝΠϧ͕খ͘͞ͳΓ΍͍͢ͷͰωοτϫʔΫΛ௨ͨ͡഑৴ʹద͍ͯ͠Δ r എܠͷಁաΛαϙʔτ ‣ ΞΠίϯ΍ϩΰʹར༻͠΍͍͢ r ΞχϝʔγϣϯΛαϙʔτ

    r छྨͷѹॖϞʔυ ‣ -PTTZʢඇՄٯѹॖʣʜࡉ෦ͷ඼࣭͸Θ͔ͣʹࣦΘΕΔ͕ඇৗʹαΠζ͕খ͘͞ͳΔ ‣ -PTTMFTTʢՄٯѹॖʣʜݩͷըૉΛ׬શʹ෮ݩͰ͖Δ 8FC1ͬͯͳʹʁ
  3. 8FC1ͬͯͳʹʁ r 8FCαΠτͰར༻͢Δը૾ͷͨΊʹ࡞ΒΕͨը૾ѹॖܗࣜ r ಉ͡ݟͨ໨ͰϑΝΠϧ͕খ͘͞ͳΓ΍͍͢ͷͰωοτϫʔΫΛ௨ͨ͡഑৴ʹద͍ͯ͠Δ r എܠͷಁաΛαϙʔτ ‣ ΞΠίϯ΍ϩΰʹར༻͠΍͍͢ r

    ΞχϝʔγϣϯΛαϙʔτ r छྨͷѹॖϞʔυ ‣ -PTTZʢඇՄٯѹॖʣʜࡉ෦ͷ඼࣭͸Θ͔ͣʹࣦΘΕΔ͕ඇৗʹαΠζ͕খ͘͞ͳΔ ‣ -PTTMFTTʢՄٯѹॖʣʜݩͷըૉΛ׬શʹ෮ݩͰ͖Δ
  4. 'R' 'I' 'F' 'F' CZUFT 3*''$POUBJOFS 'R' 'I' 'F' 'F'

    'W' 'E' 'B' 'P' σʔλߏ଄ʹ͍ͭͯ 3*'' 3FTPVSDF*OUFSDIBOHF'JMF'PSNBU
  5. 'R' 'I' 'F' 'F' CZUFT 3*''$POUBJOFS 'R' 'I' 'F' 'F'

    'W' 'E' 'B' 'P' $IVOL'PVS$$ $IVOL4J[F $IVOL1BZMPBE σʔλߏ଄ʹ͍ͭͯ 3*'' 3FTPVSDF*OUFSDIBOHF'JMF'PSNBU 8FC1)FBEFS
  6. 'R' 'I' 'F' 'F' CZUFT 3*''$POUBJOFS 'R' 'I' 'F' 'F'

    'W' 'E' 'B' 'P' 'V' 'P' '8' ' ' $IVOL4J[F $IVOL1BZMPBE 4JNQMF'JMF'PSNBU 71 -PTTZ
  7. 'R' 'I' 'F' 'F' CZUFT 3*''$POUBJOFS 'R' 'I' 'F' 'F'

    'W' 'E' 'B' 'P' 'V' 'P' '8' 'L' $IVOL1BZMPBE 4JNQMF'JMF'PSNBU 71- -PTTMFTT $IVOL4J[F
  8. 'R' 'I' 'F' 'F' CZUFT 3*''$POUBJOFS 'R' 'I' 'F' 'F'

    'W' 'E' 'B' 'P' 'V' 'P' '8' 'L' CZUFT $IVOL1BZMPBE 4JNQMF'JMF'PSNBU 71 -PTTZ CZUFT CZUFT CZUFT
  9. 'R' 'I' 'F' 'F' CZUFT 3*''$POUBJOFS 'R' 'I' 'F' 'F'

    'W' 'E' 'B' 'P' 'V' 'P' '8' 'L' CZUFT $IVOL1BZMPBE 4JNQMF'JMF'PSNBU 71- -PTTMFTT CZUFT CZUFT CZUFT
  10. 'R' 'I' 'F' 'F' CZUFT 3*''$POUBJOFS 'R' 'I' 'F' 'F'

    'W' 'E' 'B' 'P' 'V' 'P' '8' 'X' CZUF $IVOL1BZMPBE &YUFOEFE'JMF'PSNBU *$$1SP fi MF "MQIBDIBOOFM "OJNBUJPO &9*'9.1NFUBEBUB 'E' 'X' 'I' 'F' $IVOL 4J[F $IVOL 1BZMPBE
  11. 'A' 'N' 'I' 'M' 3*''$POUBJOFS 'A' 'N' 'M' 'F' &YUFOEFE'JMF'PSNBU

    *$$1SP fi MF "MQIBDIBOOFM "OJNBUJPO &9*'9.1NFUBEBUB 'A' 'N' 'M' 'F' $IVOL1BZMPBE $IVOL1BZMPBE
  12. 3*'')FBEFS3FBEFS 4BNQMF$PEF struct RIFFHeaderReader { static func read(_ data: Data)

    throws -> (header: RIFFHeader, chunks: [RIFFChunk]) { var reader = ByteReader(data) let sig = try reader.readFourCC() let riffSize = try reader.readUInt32LE() let formType = try reader.readFourCC() let logicalEnd = 8 + Int(riffSizeLE) guard logicalEnd <= data.count else { throw WebPListError.truncatedRIFF } var chunks: [RIFFChunk] = [] while reader.offset + 8 <= logicalEnd { let fourCC = try r.readFourCC() let size = try r.readUInt32LE() let payloadStart = r.offset let payloadEnd = payloadStart + Int(size) try reader.skip(Int(sizeLE)) if (size & 1) == 1 { try reader.skip(1) } } let header = RIFFHeader(signature: sig, fileSizeLE: riffSize, formType: formType) return (header, chunks) } }
  13. খ͞ͳ࣮૷Ͱʮ8FC1ը૾ʯΛ࡞Δ – 'VP8 ' ʜ-PTTZʢඇՄٯѹॖʣ – 'VP8L' ʜ-PTTMFTTʢՄٯѹॖʣ – 'ALPH'ʜ-PTTZʢඇՄٯѹॖʣϑΥʔϚοτͷΞϧϑΝ৘ใ

    r ANIM''ANMF' ʜΞχϝʔγϣϯʗΞχϝʔγϣϯϑϨʔϜ – 'ICCP' ʜ*$$ΧϥʔϓϩϑΝΠϧ r 'EXIF''XMP 'ʜϝλσʔλ
  14. – 'VP8 ' ʜ-PTTZʢඇՄٯѹॖʣ – 'VP8L' ʜ-PTTMFTTʢՄٯѹॖʣ – 'ALPH'ʜ-PTTZʢඇՄٯѹॖʣϑΥʔϚοτͷΞϧϑΝ৘ใ r

    ANIM''ANMF' ʜΞχϝʔγϣϯʗΞχϝʔγϣϯϑϨʔϜ – 'ICCP' ʜ*$$ΧϥʔϓϩϑΝΠϧ r 'EXIF''XMP 'ʜϝλσʔλ খ͞ͳ࣮૷Ͱʮ8FC1ը૾ʯΛ࡞Δ
  15. – 'VP8 ' ʜ-PTTZʢඇՄٯѹॖʣ – 'VP8L' ʜ-PTTMFTTʢՄٯѹॖʣ – 'ALPH'ʜ-PTTZʢඇՄٯѹॖʣϑΥʔϚοτͷΞϧϑΝ৘ใ r

    ANIM''ANMF' ʜΞχϝʔγϣϯʗΞχϝʔγϣϯϑϨʔϜ – 'ICCP' ʜ*$$ΧϥʔϓϩϑΝΠϧ r 'EXIF''XMP 'ʜϝλσʔλ খ͞ͳ࣮૷Ͱʮ8FC1ը૾ʯΛ࡞Δ
  16. – 'VP8 ' ʜ-PTTZʢඇՄٯѹॖʣ – 'VP8L' ʜ-PTTMFTTʢՄٯѹॖʣ – 'ALPH'ʜ-PTTZʢඇՄٯѹॖʣϑΥʔϚοτͷΞϧϑΝ৘ใ r

    ANIM''ANMF' ʜΞχϝʔγϣϯʗΞχϝʔγϣϯϑϨʔϜ – 'ICCP' ʜ*$$ΧϥʔϓϩϑΝΠϧ r 'EXIF''XMP 'ʜϝλσʔλ খ͞ͳ࣮૷Ͱʮ8FC1ը૾ʯΛ࡞Δ എܠ͕෼͔ΕͯͨΓΩϟϯόεશମͰ͸ͳ͘ ಈ͘෦෼͚ͩ֨ೲ͢ΔͳͲएׯͷ௥Ճ࣮૷͕ඞཁ
  17. – 'VP8 ' ʜ-PTTZʢඇՄٯѹॖʣ – 'VP8L' ʜ-PTTMFTTʢՄٯѹॖʣ – 'ALPH'ʜ-PTTZʢඇՄٯѹॖʣϑΥʔϚοτͷΞϧϑΝ৘ใ r

    ANIM''ANMF' ʜΞχϝʔγϣϯʗΞχϝʔγϣϯϑϨʔϜ – 'ICCP' ʜ*$$ΧϥʔϓϩϑΝΠϧ r 'EXIF''XMP 'ʜϝλσʔλ খ͞ͳ࣮૷Ͱʮ8FC1ը૾ʯΛ࡞Δ
  18. – 'VP8 ' ʜ-PTTZʢඇՄٯѹॖʣ – 'VP8L' ʜ-PTTMFTTʢՄٯѹॖʣ – 'ALPH'ʜ-PTTZʢඇՄٯѹॖʣϑΥʔϚοτͷΞϧϑΝ৘ใ r

    ANIM''ANMF' ʜΞχϝʔγϣϯʗΞχϝʔγϣϯϑϨʔϜ – 'ICCP' ʜ*$$ΧϥʔϓϩϑΝΠϧ r 'EXIF''XMP 'ʜϝλσʔλ খ͞ͳ࣮૷Ͱʮ8FC1ը૾ʯΛ࡞Δ
  19. #JU3FBEFS 4BNQMF$PEF struct BitReader { var bits: UInt32 = 0

    // Buffered bits (LSB-first). var nBits: UInt32 = 0 // Number of valid bits currently in `bits`. var reader: ByteReader init(data: Data) { self.reader = ByteReader(data) } mutating func read(_ n: UInt32) throws -> UInt32 { precondition((1...32).contains(n), "n must be in 1…32") // Refill until we have enough bits. while nBits < n { guard let byte = try reader.read() else { throw BitStreamError.unexpectedEOF } // Append the byte at the current high position. bits |= UInt32(byte) << nBits nBits += 8 } // Mask out the requested bits. let result = bits & ((1 << n) &- 1) // (&- avoids overflow trap when n == 32) // Shift away the bits we've consumed. bits >>= n nBits -= n return result } }
  20. ৭ม׵ Color Indexing Subtract Green Cross Color Color Cache Predictor

    LZ77 τʔΫϯԽ খ͞ͳ࣮૷ͰʮѹॖσʔλʯΛ࡞ͬͯΈΔ )VGGNBO$PEJOH )VGGNBO$PEJOH
  21. 4BNQMF$PEF ➡IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJXFCQFYFSDJTFT 📦 1SF fi Y$PEJOH 4VCUSBDU (SFFO -; 1SFEJDUPS

    Cross Color $PMPS *OEFYJOH $PMPS$BDIF 1SF fi Y$PEJOH 🗜📤 4VCUSBDU(SFFO 🗜📤 🗜📤 -; 🗜📤 🗜📤 1SFEJDUPS 🗜📤 🗜📤 $SPTT$PMPS 🗜📤 🗜📤 $PMPS*OEFYJOH 🗜📤 🗜📤 $PMPS$BDIF 🗜📤 🗜📤 $PNQMFUF 🗜📤 🗜📤 🗜📤 🗜📤 🗜📤 🗜📤 🗜📤
  22. ϋϑϚϯූ߸Խ ྫ#00,,&&1&3 BOOKKEEPER γϯϘϧ & 0 , # 1 3

    ग़ݱස౓       ௿ස౓ ߴස౓
  23. ϋϑϚϯූ߸Խ ྫ#00,,&&1&3 B P γϯϘϧ & 0 , 3 #

    1 ग़ݱස౓         O K R E    
  24. ϋϑϚϯූ߸Խ ྫ#00,,&&1&3 B P γϯϘϧ & 0 , 3 #

    1 ग़ݱස౓         O K R E    
  25. ϋϑϚϯූ߸Խ ྫ#00,,&&1&3 B P γϯϘϧ & 0 , 3 #

    1 ग़ݱස౓         O K R E     
  26. ϋϑϚϯූ߸Խ ྫ#00,,&&1&3 B P γϯϘϧ & 0 , 3 #

    1 ग़ݱස౓         O K R E      
  27. ϋϑϚϯූ߸Խ ྫ#00,,&&1&3 B P γϯϘϧ & 0 , 3 #

    1 ग़ݱස౓         O K R E     2 
  28. B P O K R E γϯϘϧ & 0 ,

    3 # 1 ग़ݱස౓       ූ߸ 0 0 1 0 1 1 1 0 1 0
  29. B P O K R E γϯϘϧ & 0 ,

    3 # 1 ग़ݱස౓       ූ߸ 00 0 0 1 0 1 1 1 0 1 0
  30. γϯϘϧ & 0 , 3 # 1 ग़ݱස౓  

        ූ߸ 00 10 B P O K R E 2 0 0 1 0 1 1 1 0 1 0
  31. B P O K R E γϯϘϧ & 0 ,

    3 # 1 ग़ݱස౓       ූ߸ 00 10 010 0 0 1 0 1 1 1 0 1 0
  32. γϯϘϧ & 0 , 3 # 1 ग़ݱස౓  

        ූ߸ 00 10 010 011 110 111 B P O K R E 0 0 1 0 1 1 1 0 1 0
  33. ϋϑϚϯූ߸Խ ྫ#00,,&&1&3 γϯϘϧ & 0 , 3 # 1 ग़ݱස౓

          ූ߸ 00 10 010 011 110 111 B O O K K E E P E R 100 101 101 01 01 00 00 110 00 111
  34. B P   O K R E  

           γϯϘϧ & 0 , 3 # 1 ग़ݱස౓       ූ߸ 00 10 010 011 110 111
  35. ූ߸௕ CJU CJU B P O K R E $BOPOJDBM)VGGNBO$PEJOH

    ਖ਼४ʢਖ਼نԽʣϋϑϚϯූ߸Խ CJU
  36. ූ߸௕ CJU CJU P R O B K E $BOPOJDBM)VGGNBO$PEJOH

    ਖ਼४ʢਖ਼نԽʣϋϑϚϯූ߸Խ CJU
  37. ූ߸௕ CJU CJU P R O B K E $BOPOJDBM)VGGNBO$PEJOH

    ਖ਼४ʢਖ਼نԽʣϋϑϚϯූ߸Խ CJU 00 01
  38. ූ߸௕ CJU CJU P R O B K E $BOPOJDBM)VGGNBO$PEJOH

    ਖ਼४ʢਖ਼نԽʣϋϑϚϯූ߸Խ CJU 00 01 100 101 110 111
  39. B O O K K E E P E R

    100 01 01 101 101 00 00 110 00 111 $BOPOJDBM)VGGNBO$PEJOH ਖ਼४ʢਖ਼نԽʣϋϑϚϯූ߸Խ [A:0, B:3, C:0, D:0, E:2, F:0, G:0, H:0, I:0, J:0, K:3, L:0, M:0, N:0, O:2, P:3, Q:0, R:3, S:0, T:0, U:0, V:0, W:0, X:0, Y:0, Z:0]
  40. B O O K K E E P E R

    100 01 01 101 101 00 00 110 00 111 $BOPOJDBM)VGGNBO$PEJOH ਖ਼४ʢਖ਼نԽʣϋϑϚϯූ߸Խ [0,3,0,0,2,0,0,0,0,0,3,0,0,0,2,3,0,3,0,0,0,0,0,0,0,0]
  41. γϯϘϧ         ग़ݱස౓

            ූ߸ 0 101 110 100 11111 11100 11101 11110
  42. ৭ม׵ Color Indexing Subtract Green Cross Color Color Cache Predictor

    LZ77 τʔΫϯԽ )VGGNBO$PEJOH )VGGNBO$PEJOH 3FRVJSFE 0QUJPOBM
  43. 4VCUSBDU(SFFO Τϯίʔυ func applySubtractGreenTransform(pixels: inout [NRGBA]) { for i in

    pixels.indices { pixels[i].r = pixels[i].r &- pixels[i].g pixels[i].b = pixels[i].b &- pixels[i].g } }
  44. 4VCUSBDU(SFFO σίʔυʢٯม׵ʣ func inverseSubtractGreen(_ pixel: [UInt8]) -> [UInt8] { var

    out = pixel var i = 0 while p < out.count { let g = out[i + 1] // green out[i + 0] &+= g // red += green out[i + 2] &+= g // blue += green I += 4 } return out }
  45. 4VCUSBDU(SFFO ޮՌ [52, 50, 47] [106, 110, 97] [147, 150,

    156] [202, 200, 197] [2, 50, -3] [-4, 100, -3] [-3, 150, 6] [2, 200, -3]
  46. 3FGFSFODFT r 4BNQMF$PEF IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJXFCQFYFSDJTFT r "OJNBHFGPSNBUGPSUIF8FC c 8FC1 c (PPHMFGPS%FWFMPQFST

    IUUQTEFWFMPQFSTHPPHMFDPNTQFFEXFCQ r MJCXFCQ IUUQTDISPNJVNHPPHMFTPVSDFDPNXFCNMJCXFCQ r MJCXFCQUFTUEBUB IUUQTDISPNJVNHPPHMFTPVSDFDPNXFCNMJCXFCQUFTUEBUB