Skip to content
This repository has been archived by the owner on Jun 24, 2024. It is now read-only.

Commit

Permalink
Implement 16bit color decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
hyazinthh committed Jan 10, 2023
1 parent 5692b05 commit 2f2b94d
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 15 deletions.
16 changes: 7 additions & 9 deletions src/Aardvark.PixImage.Pfim.Tests/Dds.fs
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ module DdsTests =
[| 0; 28; 74 |]
[| 189; 0; 16 |]
[| 16; 174; 0 |]
[| 172; 161; 0 |]
[| 173; 162; 0 |]
|]
|> Array.map (Array.map uint8)

let rgb5a1 =
[|
[| 0; 32; 74; 255 |]
[| 0; 33; 74; 255 |]
[| 189; 0; 16; 255 |]
[| 16; 172; 0; 255 |]
[| 172; 164; 0; 255 |]
[| 16; 173; 0; 255 |]
[| 173; 165; 0; 255 |]
|]
|> Array.map (Array.map uint8)

Expand Down Expand Up @@ -252,11 +252,9 @@ module DdsTests =
// Broken: Pfim parses as grayscale
// testCase "Dds.R3G3B2 mipmapped" Cases.r3g3b2Mipmapped

// TODO: Implement us!
//testCase "Dds.R5G6B5 mipmapped" Cases.r5g6b5Mipmapped
//testCase "Dds.RGB5A1 mipmapped" Cases.rgb5a1Mipmapped
//testCase "Dds.RGBA4 mipmapped" Cases.rgba4Mipmapped

testCase "Dds.R5G6B5 mipmapped" Cases.r5g6b5Mipmapped
testCase "Dds.RGB5A1 mipmapped" Cases.rgb5a1Mipmapped
testCase "Dds.RGBA4 mipmapped" Cases.rgba4Mipmapped
testCase "Dds.L8 mipmapped" Cases.l8Mipmapped

// Broken: Pfim parses as r5g5b5a1
Expand Down
120 changes: 114 additions & 6 deletions src/Aardvark.PixImage.Pfim/PixImagePfim.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

open System
open System.IO
open FSharp.NativeInterop
open Pfim

#nowarn "9"
Expand All @@ -21,10 +22,41 @@ module private PixImagePfimImpl =
if isNull x.MipMaps then 1
else x.MipMaps.Length + 1

[<AutoOpen>]
module private ColorUtilities =

module Bits =

module Tables =

let expand1 =
Array.init 2 (fun i -> uint8 (i * 255))

let expand4 =
Array.init 16 (fun i -> uint8 (i * 17))

let expand5 =
Array.init 32 (fun i -> uint8 ((i <<< 3) ||| (i >>> 2)))

let expand6 =
Array.init 64 (fun i -> uint8 ((i <<< 2) ||| (i >>> 4)))

let inline expand1 x =
Tables.expand1.[int x]

let inline expand4 x =
Tables.expand4.[int x]

let inline expand5 x =
Tables.expand5.[int x]

let inline expand6 x =
Tables.expand6.[int x]

// Note: The default allocator of Pfim just allocates a new array, so
// we can just use it without worrying about it being reused when the original
// IImage is disposed.
let private of8Bit (format : Col.Format) (info : MipMapOffset) (src : uint8[]) =
let private of8Bit (format : Col.Format) (info : MipMapOffset) (src : uint8[]) : PixImage =
let channels =
format.ChannelCount()

Expand All @@ -38,17 +70,93 @@ module private PixImagePfimImpl =
)
)

PixImage<uint8>(format, volume) :> PixImage
PixImage<uint8>(format, volume)

let private of16Bit (format : Col.Format) (decode : uint16 -> C4b) (info : MipMapOffset) (src : uint8[]) : PixImage =
if info.Stride % 2 <> 0 then
failwith $"Stride is odd ({info.Stride})."

let pi = PixImage<uint8>(format, int64 info.Width, int64 info.Height)

pinned src (fun src ->
let srcVolume =
NativeVolume<uint16>(
NativePtr.ofNativeInt (src + nativeint info.DataOffset),
VolumeInfo(
0L,
V3l(info.Width, info.Height, 1),
V3l(1, info.Stride / 2, 1)
)
)

PixImage.pin pi (fun dst ->
let dstVolume =
NativeVolume<uint8>(
dst.Pointer,
VolumeInfo(dst.Origin, dst.Size.XYI, dst.Delta)
)

(srcVolume, dstVolume) ||> NativeVolume.iterPtr2 (fun _ src dst ->
let color = decode <| NativePtr.read src
NativePtr.set dst 0 color.B
NativePtr.set dst 1 color.G
NativePtr.set dst 2 color.R
NativePtr.set dst 3 color.A
)
)
)

pi

let private ofRGBA16 =
of16Bit Col.Format.BGRA (fun pixel ->
let b = pixel &&& 0xFus
let g = (pixel >>> 4) &&& 0xFus
let r = (pixel >>> 8) &&& 0xFus
let a = (pixel >>> 12) &&& 0xFus
C4b(
Bits.expand4 r,
Bits.expand4 g,
Bits.expand4 b,
Bits.expand4 a
)
)

let private ofR5G5B5A1 (withAlpha : bool) =
of16Bit Col.Format.BGRA (fun pixel ->
let b = pixel &&& 0x1Fus
let g = (pixel >>> 5) &&& 0x1Fus
let r = (pixel >>> 10) &&& 0x1Fus
let a = (pixel >>> 15) &&& 0x1us
C4b(
Bits.expand5 r,
Bits.expand5 g,
Bits.expand5 b,
if withAlpha then Bits.expand1 a else 255uy
)
)

let private ofR5G6B5 =
of16Bit Col.Format.BGR (fun pixel ->
let b = pixel &&& 0x1Fus
let g = (pixel >>> 5) &&& 0x3Fus
let r = (pixel >>> 11) &&& 0x1Fus
C4b(
Bits.expand5 r,
Bits.expand6 g,
Bits.expand5 b
)
)

// TODO: Implement missing formats
let private levelToPixImageTable : (ImageFormat -> (MipMapOffset -> uint8[] -> PixImage) option) =
LookupTable.lookupTable' [
ImageFormat.Rgb8, of8Bit Col.Format.Gray
ImageFormat.Rgb24, of8Bit Col.Format.BGR
ImageFormat.Rgba32, of8Bit Col.Format.BGRA
//ImageFormat.R5g5b5
//ImageFormat.R5g6b5
//ImageFormat.R5g5b5a1
ImageFormat.Rgba16, ofRGBA16
ImageFormat.R5g5b5, ofR5G5B5A1 false
ImageFormat.R5g6b5, ofR5G6B5
ImageFormat.R5g5b5a1, ofR5G5B5A1 true
]

let private levelToPixImage (format : ImageFormat) =
Expand Down

0 comments on commit 2f2b94d

Please sign in to comment.