/* * Panasonic RW2 raw image decoder * Copyright (c) 2026 Lynne * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * Panasonic RW2 decoder, RawFormat 8 only (S1II, S5II, GH6). * Bit-exact with LibRaw's pana8.cpp, quirks included. * * Input is the whole file as one packet. */ #include "libavutil/internal.h" #include "libavutil/buffer.h" #include "libavutil/mem.h" #include "libavutil/raw_color_params.h" #include "libavutil/reverse.h" #define BITSTREAM_READER_LE #define CACHED_BITSTREAM_READER 1 #include "avcodec.h" #include "bytestream.h" #include "codec_internal.h" #include "decode.h" #include "exif.h" #include "exif_internal.h" #include "get_bits.h" #include "hwaccel_internal.h" #include "thread.h" #include "tiff_common.h" #include "rw2.h" #define RW2_VLC_BITS 11 #define RW2_JOINT_BITS 16 /* longest code with its magnitude folded in */ #define RW2_SYM_DELTA 0x2000 /* joint symbols: delta + RW2_SYM_DELTA */ #define RW2_SYM_ESC 0x4000 /* other categories: RW2_SYM_ESC + category */ static av_always_inline int get_delta(const RW2DecContext *s, GetBitContext *bc, int *delta) { int sym = get_vlc2(bc, s->vlc.table, RW2_VLC_BITS, 2); if (sym < RW2_SYM_ESC) { if (sym < 0) return AVERROR_INVALIDDATA; *delta = sym - RW2_SYM_DELTA; return 0; } /* Escapes read k - shift magnitude bits, MSB-first, the sign leading */ int k = sym - RW2_SYM_ESC; int shift = s->h.c.code_shift[k], extra = k - shift; int d = 0; if (extra > 0) { unsigned v = get_bits(bc, extra); unsigned m = (ff_reverse[v & 0xFF] << 8 | ff_reverse[v >> 8]) >> (16 - extra); d = (m << shift) & 0xFFFF; if (!(v & 1)) d += !shift - (1 << k); } else if (k && !show_bits(bc, 1)) { /* No magnitude, but LibRaw still tests the next bit for the sign */ d = !shift - (1 << k); } *delta = d + (shift ? 1 << (shift - 1) : 0); return 0; } static int gamma_curve(const RW2Coding *c, uint32_t v) { int seg = 0; while (seg < 5 && v >= c->gamma_point[seg + 1]) seg++; /* LibRaw only keeps the second half of each 0x3A pair, which makes the * output offset of every segment 0 */ uint32_t sh = c->gamma_shift[seg]; uint32_t r = v - c->gamma_point[seg]; if ((sh & 0x1F) == 31) r = seg == 5 ? 0xFFFF : 0; else if (sh & 0x10) r <<= sh & 0xF; else if ((sh & 0x1F) == 15) r = 0; else if (sh & 0x1F) r = (r + (1 << ((sh & 0x1F) - 1))) >> (sh & 0x1F); return FFMIN(r, c->datamax); } static int build_tables(RW2DecContext *s) { const RW2Coding *c = &s->h.c; int nb = 0; /* A code per magnitude where both fit and nothing is shifted, the rest escape */ for (int k = 0; k < 17; k++) { int len = c->code_len[k], code = c->code_val[k]; if (!len) continue; if (c->code_shift[k] || k >= RW2_JOINT_CATS || len + k > RW2_JOINT_BITS) { s->codes[nb++] = (RW2Code){ len, code, RW2_SYM_ESC + k }; continue; } for (int m = 0; m < (1 << k); m++) { int d = k && !(m >> (k - 1)) ? m + 1 - (1 << k) : m; s->codes[nb++] = (RW2Code){ len + k, code << k | m, RW2_SYM_DELTA + d }; } } ff_vlc_free(&s->vlc); int ret = ff_vlc_init_sparse(&s->vlc, RW2_VLC_BITS, nb, &s->codes[0].len, sizeof(*s->codes), 1, &s->codes[0].code, sizeof(*s->codes), 2, &s->codes[0].sym, sizeof(*s->codes), 2, VLC_INIT_OUTPUT_LE); if (ret < 0) return ret; /* The gamma LUT, an identity on every file seen so far */ s->use_gamma = 0; for (int i = 0; i <= c->datamax; i++) { s->gamma[i] = gamma_curve(c, i); s->use_gamma |= s->gamma[i] != i; } return 0; } static int decode_stripe(AVCodecContext *avctx, void *tdata, int jobnr, int threadnr) { RW2DecContext *s = avctx->priv_data; AVFrame *frame = tdata; const RW2Stripe *st = &s->h.stripes[jobnr]; const ptrdiff_t stride = frame->linesize[0]; const uint16_t *gamma = s->use_gamma ? s->gamma : NULL; const int datamax = s->h.c.datamax; int line_base[4], cur[4]; GetBitContext bc; int ret; ret = init_get_bits(&bc, s->payload + st->offset, st->size_bits); if (ret < 0) return ret; for (int i = 0; i < 4; i++) line_base[i] = s->h.c.initial[i]; for (int y = 0; y < st->height; y += 2) { uint16_t *dst0 = (uint16_t *)(frame->data[0] + y*stride) + st->left; uint16_t *dst1 = (uint16_t *)((uint8_t *)dst0 + stride); /* Each sample predicts from the same position in the previous 2x2 */ memcpy(cur, line_base, sizeof(cur)); for (int x = 0; x < st->width; x += 2) { /* Coding order: top-left, bottom-left, top-right, bottom-right */ static const uint8_t slot[4] = { 0, 2, 1, 3 }; for (int i = 0; i < 4; i++) { int delta, j = slot[i]; if ((ret = get_delta(s, &bc, &delta)) < 0) return ret; cur[j] = av_clip(cur[j] + delta, 0, datamax); } if (gamma) { dst0[x] = gamma[cur[0]]; dst0[x + 1] = gamma[cur[1]]; dst1[x] = gamma[cur[2]]; dst1[x + 1] = gamma[cur[3]]; } else { dst0[x] = cur[0]; dst0[x + 1] = cur[1]; dst1[x] = cur[2]; dst1[x + 1] = cur[3]; } if (!x) memcpy(line_base, cur, sizeof(line_base)); } if (get_bits_left(&bc) < 0) return AVERROR_INVALIDDATA; } return 0; } static int read_array_count(GetByteContext *gb, unsigned type, unsigned count, unsigned expected, int max) { int n; if (type != AV_TIFF_UNDEFINED || count != expected) return 0; n = bytestream2_get_le16(gb); return FFMIN(n, max); } /* Tag layouts and clamps follow LibRaw */ static int parse_header(RW2Header *h, const uint8_t *data, int size) { RW2Coding *c = &h->c; GetByteContext gb; memset(h, 0, sizeof(*h)); bytestream2_init(&gb, data, size); if (bytestream2_get_le32(&gb) != MKTAG('I', 'I', 'U', '\0')) return AVERROR_INVALIDDATA; int64_t ifd0 = bytestream2_get_le32(&gb); if (ifd0 < 8 || ifd0 + 2 > size) return AVERROR_INVALIDDATA; bytestream2_seek(&gb, ifd0, SEEK_SET); int nb_entries = bytestream2_get_le16(&gb); if (nb_entries > RW2_MAX_IFD0_ENTRIES || bytestream2_get_bytes_left(&gb) < nb_entries*12) return AVERROR_INVALIDDATA; int64_t ifd_end = ifd0 + 2 + 12*(int64_t)nb_entries + 4; h->exif_off = ifd0; h->exif_end = size; for (int i = 0; i < nb_entries; i++) { unsigned tag, type, count, n, v; int next; if (ff_tread_tag(&gb, 1, &tag, &type, &count, &next) < 0) goto next; /* Keep the standard tags verbatim for EXIF in this same walk, leaving * out the Panasonic-private ones and the orientation that is exported * as a display matrix; the previews mark where the metadata ends */ const uint8_t *rec = data + ifd0 + 2 + 12*i; if ((tag == 0x2E || tag == 0x111 || tag == 0x127) && AV_RL32(rec + 8) >= ifd_end) h->exif_end = FFMIN(h->exif_end, (int64_t)AV_RL32(rec + 8)); if (tag >= 0x100 && tag != 0x111 && tag != 0x112 && tag != 0x117 && !(tag >= 0x118 && tag <= 0x127)) { memcpy(h->exif + h->exif_count*12, rec, 12); h->exif_count++; } switch (tag) { case 0x02: h->raw_width = ff_tget(&gb, type, 1); break; case 0x03: h->raw_height = ff_tget(&gb, type, 1); break; case 0x04: case 0x05: case 0x06: case 0x07: h->border[tag - 0x04] = ff_tget(&gb, type, 1); break; case 0x09: h->cfa = ff_tget(&gb, type, 1); break; case 0x0A: h->bpp = ff_tget(&gb, type, 1); break; case 0x0E: case 0x0F: case 0x10: h->linearity[tag - 0x0E] = ff_tget(&gb, type, 1); break; case 0x1C: case 0x1D: case 0x1E: h->black[tag - 0x1C] = ff_tget(&gb, type, 1); break; case 0x24: case 0x25: case 0x26: h->wb[tag - 0x24] = ff_tget(&gb, type, 1); break; case 0x2D: h->raw_format = ff_tget(&gb, type, 1); break; case 0x39: n = read_array_count(&gb, type, count, 26, 6); for (int j = 0; j < n; j++) c->gamma_shift[j] = bytestream2_get_le32(&gb); break; case 0x3A: n = read_array_count(&gb, type, count, 26, 6); for (int j = 0; j < n; j++) { bytestream2_skip(&gb, 2); c->gamma_point[j] = bytestream2_get_le16(&gb); } break; case 0x3B: c->datamax = ff_tget(&gb, type, 1) & 0xFFFF; break; case 0x3C: case 0x3D: case 0x3E: case 0x3F: c->initial[tag - 0x3C] = ff_tget(&gb, type, 1) & 0xFFFF; break; case 0x40: n = read_array_count(&gb, type, count, 70, 17); for (int k = 0; k < n; k++) { v = bytestream2_get_le16(&gb); int len = FFMIN(v, 16); v = bytestream2_get_le16(&gb); int code = FFMIN(v, 0xFFF) & ((1 << len) - 1); /* LibRaw takes the first match, drop the codes it never reaches */ for (int j = 0; j < k && len; j++) if (c->code_len[j] && c->code_len[j] <= len && code >> (len - c->code_len[j]) == c->code_val[j]) len = 0; c->code_len[k] = len; c->code_val[k] = code; } break; case 0x41: n = read_array_count(&gb, type, count, 36, 17); for (int j = 0; j < n; j++) { v = bytestream2_get_le16(&gb); c->code_shift[j] = FFMIN(v, 64) & 0x1F; } break; case 0x42: v = ff_tget(&gb, type, 1); h->nb_stripes = FFMIN(v, RW2_MAX_STRIPES); break; case 0x44: case 0x45: case 0x46: n = read_array_count(&gb, type, count, 50, RW2_MAX_STRIPES); for (int j = 0; j < n; j++) { v = bytestream2_get_le32(&gb); if (tag == 0x44) h->stripes[j].offset = v; else if (tag == 0x45) h->stripes[j].left = v; else h->stripes[j].size_bits = v; } break; case 0x47: case 0x48: n = read_array_count(&gb, type, count, 26, RW2_MAX_STRIPES); for (int j = 0; j < n; j++) { v = bytestream2_get_le16(&gb); if (tag == 0x47) h->stripes[j].width = v; else h->stripes[j].height = v; } break; case 0x112: h->orientation = ff_tget(&gb, type, 1); break; } next: bytestream2_seek(&gb, next, SEEK_SET); } return 0; } static int check_stripes(AVCodecContext *avctx, const RW2Header *h, int payload_size) { uint32_t left = 0; if (h->nb_stripes < 1) return AVERROR_INVALIDDATA; /* Stripes must tile the image exactly */ for (int i = 0; i < h->nb_stripes; i++) { const RW2Stripe *st = &h->stripes[i]; if (st->left != left || st->height != h->raw_height || !st->width || (st->width & 1) || st->width > h->raw_width - left || !st->size_bits || st->size_bits > INT_MAX - 7 || st->offset > payload_size || (st->size_bits + 7) >> 3 > payload_size - st->offset) { av_log(avctx, AV_LOG_ERROR, "Invalid stripe %d\n", i); return AVERROR_INVALIDDATA; } left += st->width; } if (left != h->raw_width) return AVERROR_INVALIDDATA; return 0; } static enum AVPixelFormat get_pixel_format(AVCodecContext *avctx, enum AVPixelFormat pix_fmt) { enum AVPixelFormat pix_fmts[] = { pix_fmt, AV_PIX_FMT_NONE, }; return ff_get_format(avctx, pix_fmts); } /* The TIFF and EXIF tags of the file. Panasonic's own tags, below 0x100 and * at 0x118 - 0x127, hold the raw parameters exported as side data and the * previews; the strips point into the file. */ static int export_exif(AVCodecContext *avctx, AVFrame *frame, const uint8_t *data, int size) { const RW2Header *h = &((RW2DecContext *)avctx->priv_data)->h; int64_t off = h->exif_off, keep = FFMIN(h->exif_end, size); if (!h->exif_count || off + 2 + (int64_t)h->exif_count*12 + 4 > keep) return 0; /* The metadata precedes the embedded previews, so attach it whole, pointing * IFD0 at the standard tags gathered during the header walk */ uint8_t *buf = av_memdup(data, keep); if (!buf) return AVERROR(ENOMEM); AV_WL16(buf + 2, 42); /* "IIU\0" -> TIFF */ AV_WL16(buf + off, h->exif_count); memcpy(buf + off + 2, h->exif, h->exif_count * 12); AV_WL32(buf + off + 2 + h->exif_count*12, 0); /* no next IFD */ AVBufferRef *bref = av_buffer_create(buf, keep, NULL, NULL, 0); if (!bref) { av_free(buf); return AVERROR(ENOMEM); } return ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_EXIF, &bref); } static int export_metadata(AVCodecContext *avctx, AVFrame *frame, const uint8_t *hdr, int hdr_size) { RW2DecContext *s = avctx->priv_data; const RW2Header *h = &s->h; int ret; /* Even crops keep the CFA phase */ if (h->border[0] >= 0 && h->border[1] >= 0 && h->border[0] < h->border[2] && h->border[2] <= h->raw_height && h->border[1] < h->border[3] && h->border[3] <= h->raw_width) { frame->crop_top = h->border[0] & ~1; frame->crop_left = h->border[1] & ~1; frame->crop_bottom = (h->raw_height - h->border[2]) & ~1; frame->crop_right = (h->raw_width - h->border[3]) & ~1; } if (h->orientation > 1 && h->orientation <= 8) { AVFrameSideData *sd; ret = ff_frame_new_side_data(avctx, frame, AV_FRAME_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9, &sd); if (ret < 0) return ret; if (sd) av_exif_orientation_to_matrix((int32_t *)sd->data, h->orientation); } /* Per-channel black levels are not representable, use green's */ int white = FFMAX3(h->linearity[0], h->linearity[1], h->linearity[2]); if (!white) white = (1 << h->bpp) - 1; AVRawColorParams *rcp = av_raw_color_params_create_side_data(frame); if (!rcp) return AVERROR(ENOMEM); rcp->black_level = av_make_q(h->black[1], 65535); rcp->white_level = av_make_q(white, 65535); if (h->wb[1]) { rcp->type = AV_RAW_COLOR_PARAMS_RW2; rcp->codec.rw2.wb_red = av_make_q(h->wb[0], h->wb[1]); rcp->codec.rw2.wb_blue = av_make_q(h->wb[2], h->wb[1]); } return export_exif(avctx, frame, hdr, hdr_size); } static int decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt) { RW2DecContext *s = avctx->priv_data; RW2Header *h = &s->h; int ret; static const enum AVPixelFormat cfa_fmts[] = { AV_PIX_FMT_BAYER_RGGB16, AV_PIX_FMT_BAYER_GRBG16, AV_PIX_FMT_BAYER_GBRG16, AV_PIX_FMT_BAYER_BGGR16, }; ret = parse_header(h, avpkt->data, avpkt->size); if (ret < 0) return ret; if (h->raw_format != 8) { if (!h->raw_format) return AVERROR_INVALIDDATA; avpriv_request_sample(avctx, "RawFormat %d", h->raw_format); return AVERROR_PATCHWELCOME; } if (h->cfa < 1 || h->cfa > 4 || h->bpp < 8 || h->bpp > 16 || (h->raw_width & 1) || (h->raw_height & 1)) return AVERROR_INVALIDDATA; ret = ff_set_dimensions(avctx, h->raw_width, h->raw_height); if (ret < 0) return ret; s->payload = avpkt->data; ret = check_stripes(avctx, h, avpkt->size); if (ret < 0) return ret; if (cfa_fmts[h->cfa - 1] != s->pix_fmt) { s->pix_fmt = cfa_fmts[h->cfa - 1]; ret = get_pixel_format(avctx, s->pix_fmt); if (ret < 0) return ret; avctx->pix_fmt = ret; } avctx->bits_per_raw_sample = h->bpp; if (avctx->skip_frame >= AVDISCARD_ALL) return avpkt->size; ret = ff_thread_get_buffer(avctx, frame, 0); if (ret < 0) return ret; s->frame = frame; ret = ff_hwaccel_frame_priv_alloc(avctx, &s->hwaccel_picture_private); if (ret < 0) return ret; ff_thread_finish_setup(avctx); if (avctx->hwaccel) { const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); ret = hwaccel->start_frame(avctx, avpkt->buf, avpkt->data, avpkt->size); if (ret < 0) return ret; for (int i = 0; i < h->nb_stripes; i++) { const RW2Stripe *st = &h->stripes[i]; ret = hwaccel->decode_slice(avctx, s->payload + st->offset, (st->size_bits + 7) >> 3); if (ret < 0) return ret; } ret = hwaccel->end_frame(avctx); if (ret < 0) return ret; av_refstruct_unref(&s->hwaccel_picture_private); } else { int rets[RW2_MAX_STRIPES]; ret = build_tables(s); if (ret < 0) return ret; avctx->execute2(avctx, decode_stripe, frame, rets, h->nb_stripes); for (int i = 0; i < h->nb_stripes; i++) { if (rets[i] < 0) { av_log(avctx, AV_LOG_ERROR, "Error decoding stripe %d\n", i); return rets[i]; } } } frame->pict_type = AV_PICTURE_TYPE_I; frame->flags |= AV_FRAME_FLAG_KEY; ret = export_metadata(avctx, frame, avpkt->data, avpkt->size); if (ret < 0) return ret; *got_frame = 1; return avpkt->size; } #if HAVE_THREADS static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) { RW2DecContext *ssrc = src->priv_data; RW2DecContext *sdst = dst->priv_data; sdst->pix_fmt = ssrc->pix_fmt; return 0; } #endif static av_cold int decode_init(AVCodecContext *avctx) { RW2DecContext *s = avctx->priv_data; avctx->color_trc = AVCOL_TRC_LINEAR; avctx->color_primaries = AVCOL_PRI_UNSPECIFIED; avctx->colorspace = AVCOL_SPC_UNSPECIFIED; s->pix_fmt = AV_PIX_FMT_NONE; return 0; } static av_cold int decode_end(AVCodecContext *avctx) { RW2DecContext *s = avctx->priv_data; av_refstruct_unref(&s->hwaccel_picture_private); ff_vlc_free(&s->vlc); return 0; } const FFCodec ff_rw2_decoder = { .p.name = "rw2", CODEC_LONG_NAME("Panasonic RW2"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_RW2, .priv_data_size = sizeof(RW2DecContext), .init = decode_init, .close = decode_end, FF_CODEC_DECODE_CB(decode_frame), UPDATE_THREAD_CONTEXT(update_thread_context), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, };