package rle import ( "fmt" "unsafe" ) func decompress2(output *[]uint8, width, height int, input []uint8, size int) bool { needed := width * height var out []uint16 if v := decompress2Pool.Get(); v != nil { out = v.([]uint16) if cap(out) < needed { out = make([]uint16, needed) } else { out = out[:needed] } } else { out = make([]uint16, needed) } defer func() { decompress2Pool.Put(out[:cap(out)]) }() var ( prevline, line, count int offset, code int x int = width opcode int lastopcode int = -1 insertmix, bicolour, isfillormix bool mixmask, mask uint8 colour1, colour2 uint16 mix uint16 = 0xffff fom_mask uint8 ) for len(input) != 0 { fom_mask = 0 code = CVAL(&input) opcode = code >> 4 /* Handle different opcode forms */ switch opcode { case 0xc, 0xd, 0xe: opcode -= 6 count = code & 0xf offset = 16 break case 0xf: opcode = code & 0xf if opcode < 9 { count = CVAL(&input) count |= CVAL(&input) << 8 } else { count = 1 if opcode < 0xb { count = 8 } } offset = 0 break default: opcode >>= 1 count = code & 0x1f offset = 32 break } /* Handle strange cases for counts */ if offset != 0 { isfillormix = ((opcode == 2) || (opcode == 7)) if count == 0 { if isfillormix { count = CVAL(&input) + 1 } else { count = CVAL(&input) + offset } } else if isfillormix { count <<= 3 } } /* Read preliminary data */ switch opcode { case 0: /* Fill */ if (lastopcode == opcode) && !((x == width) && (prevline == 0)) { insertmix = true } break case 8: /* Bicolour */ CVAL2(&input, &colour1) CVAL2(&input, &colour2) break case 3: /* Colour */ CVAL2(&input, &colour2) break case 6: /* SetMix/Mix */ fallthrough case 7: /* SetMix/FillOrMix */ CVAL2(&input, &mix) opcode -= 5 break case 9: /* FillOrMix_1 */ mask = 0x03 opcode = 0x02 fom_mask = 3 break case 0x0a: /* FillOrMix_2 */ mask = 0x05 opcode = 0x02 fom_mask = 5 break } lastopcode = opcode mixmask = 0 /* Output body */ for count > 0 { if x >= width { if height <= 0 { return false } x = 0 height-- prevline = line line = height * width } switch opcode { case 0: /* Fill */ if insertmix { if prevline == 0 { out[x+line] = mix } else { out[x+line] = out[prevline+x] ^ mix } insertmix = false count-- x++ } n := min(count, width-x) if prevline == 0 { clear(out[x+line : x+line+n]) } else { copy(out[x+line:x+line+n], out[prevline+x:prevline+x+n]) } count -= n x += n break case 1: /* Mix */ n := min(count, width-x) if prevline == 0 { seg := out[x+line : x+line+n] for i := range seg { seg[i] = mix } } else { src := out[prevline+x : prevline+x+n] dst := out[x+line : x+line+n] for i := range dst { dst[i] = src[i] ^ mix } } count -= n x += n break case 2: /* Fill or Mix */ if prevline == 0 { for count > 0 && x < width { mixmask <<= 1 if mixmask == 0 { mask = fom_mask if fom_mask == 0 { mask = uint8(CVAL(&input)) mixmask = 1 } } if mask&mixmask != 0 { out[x+line] = mix } else { out[x+line] = 0 } count-- x++ } } else { for count > 0 && x < width { mixmask = mixmask << 1 if mixmask == 0 { mask = fom_mask if fom_mask == 0 { mask = uint8(CVAL(&input)) mixmask = 1 } } if mask&mixmask != 0 { out[x+line] = out[prevline+x] ^ mix } else { out[x+line] = out[prevline+x] } count-- x++ } } break case 3: /* Colour */ n := min(count, width-x) seg := out[x+line : x+line+n] for i := range seg { seg[i] = colour2 } count -= n x += n break case 4: /* Copy */ for count > 0 && x < width { n := min(count, width-x) if len(input) < n*2 { return false } copy(out[x+line:x+line+n], unsafe.Slice((*uint16)(unsafe.Pointer(&input[0])), n)) input = input[n*2:] count -= n x += n } break case 8: /* Bicolour */ for count > 0 && x < width { if bicolour { out[x+line] = colour2 bicolour = false } else { out[x+line] = colour1 bicolour = true count++ } count-- x++ } break case 0xd: /* White */ n2 := min(count, width-x) seg2 := out[x+line : x+line+n2] for i := range seg2 { seg2[i] = 0xffff } count -= n2 x += n2 break case 0xe: /* Black */ n3 := min(count, width-x) clear(out[x+line : x+line+n3]) count -= n3 x += n3 break default: fmt.Printf("bitmap opcode 0x%x\n", opcode) return false } } } outBytes := *output for i, v := range out { outBytes[i*2] = byte(v >> 8) outBytes[i*2+1] = byte(v) } return true } // /* 3 byte bitmap decompress */