[go: up one dir, main page]

File: framebuffer.cc

package info (click to toggle)
timg 1.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 2,100 kB
  • sloc: ansic: 8,017; cpp: 4,966; pascal: 166; sh: 13; makefile: 9
file content (151 lines) | stat: -rw-r--r-- 5,396 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; -*-
// (c) 2016 Henner Zeller <h.zeller@acm.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://gnu.org/licenses/gpl-2.0.txt>

#include "framebuffer.h"

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>

namespace timg {

rgba_t rgba_t::ParseColor(const char *color) {
    if (!color) return {0, 0, 0, 0};

        // If it is a named color, convert it first to its #rrggbb string.
#include "html-colors.inc"
    for (const auto &c : html_colors) {
        if (strcasecmp(color, c.name) == 0) {
            color = c.translation;
            break;
        }
    }
    uint32_t r, g, b;
    if ((sscanf(color, "#%02x%02x%02x", &r, &g, &b) == 3) ||
        (sscanf(color, "rgb(%d, %d, %d)", &r, &g, &b) == 3) ||
        (sscanf(color, "rgb(0x%x, 0x%x, 0x%x)", &r, &g, &b) == 3)) {
        if (r > 255) r = 255;
        if (g > 255) g = 255;
        if (b > 255) b = 255;
        return {(uint8_t)r, (uint8_t)g, (uint8_t)b, 0xff};
    }
    if (strcasecmp(color, "none") != 0)  // 'allowed' non-color. Don't complain.
        fprintf(stderr, "Couldn't parse color '%s'\n", color);
    return {0, 0, 0, 0};
}

// Observed sws_scale() sometimes writing beyond the allocated size (maybe ssse3
// alignment issues ?). To prevent this being a crash situation, just allocate
// a little more to have enough contiguous space to write beyond the boundary.
// It typically only seems to be a few bytes more, but be sure and allocate
// a full row.
static constexpr int SWS_SCRATCH_ADDITIONAL_ROW = 1;

Framebuffer::Framebuffer(int w, int h, const rgba_t *from_data)
    : width_(w),
      height_(h),
      pixels_(new rgba_t[width_ * (height_ + SWS_SCRATCH_ADDITIONAL_ROW)]),
      end_(pixels_ + width_ * height_) {
    strides_[0] = (int)sizeof(rgba_t) * width_;
    strides_[1] = 0;  // empty sentinel value.
    if (from_data) {
        memcpy(pixels_, from_data, sizeof(rgba_t) * width_ * height_);
    }
    else {
        Clear();
    }
}

Framebuffer::Framebuffer(int w, int h) : Framebuffer(w, h, nullptr) {}

Framebuffer::Framebuffer(const Framebuffer &other)
    : Framebuffer(other.width(), other.height(), other.pixels_) {}

Framebuffer::~Framebuffer() {
    delete[] row_data_;
    delete[] pixels_;
}

void Framebuffer::SetPixel(int x, int y, rgba_t value) {
    if (x < 0 || x >= width() || y < 0 || y >= height()) return;
    pixels_[width_ * y + x] = value;
}

rgba_t Framebuffer::at(int x, int y) const {
    assert(x >= 0 && x < width() && y >= 0 && y < height());
    return pixels_[width_ * y + x];
}

void Framebuffer::Clear() {
    memset(pixels_, 0, sizeof(*pixels_) * width_ * height_);
}

uint8_t **Framebuffer::row_data() {
    if (!row_data_) {
        row_data_ = new uint8_t *[height_ + 1];
        for (int i = 0; i < height_; ++i)
            row_data_[i] = (uint8_t *)pixels_ + i * width_ * sizeof(rgba_t);
        row_data_[height_] = nullptr;  // empty sentinel value.
    }
    return row_data_;
}

void Framebuffer::AlphaComposeBackground(const bgcolor_query &get_bg,
                                         rgba_t pattern_col, int pwidth,
                                         int pheight, int start_row) {
    if (!get_bg) return;  // -b none

    iterator pos = begin() + (start_row * width());
    for (/**/; pos < end(); ++pos) {
        if (pos->a < 0xff) break;  // First pixel that is transparent.
    }
    if (pos >= end()) return;  // Nothing transparent all the way to the end.

    // Need to do alpha blending, so only now we have to retrieve the bgcolor.
    const rgba_t bgcolor = get_bg();
    if (bgcolor.a == 0x00) return;  // nothing to do.

    // Fast path if we don't have a pattern color.
    if (pattern_col.a == 0x00 || pattern_col == bgcolor || pwidth <= 0 ||
        pheight <= 0) {
        const LinearColor bg(bgcolor);
        for (/**/; pos < end(); ++pos) {
            if (pos->a == 0xff) continue;
            *pos = LinearColor(*pos).AlphaBlend(bg).repack();
        }
        return;
    }

    // If we have a pattern color, use that as alternating choice.
    const LinearColor bg_choice[2] = {bgcolor, pattern_col};

    // Pos moved to the first pixel that required alpha blending.
    // From this pos, we need to recover the x/y position to be in the
    // right place for the checkerboard pattern.
    const int start_x = (pos - begin()) % width_;
    const int start_y = (pos - begin()) / width_;
    for (int y = start_y; y < height_; ++y) {
        const int y_pattern_pos = y / pheight;
        for (int x = (y == start_y ? start_x : 0); x < width_; ++x, pos++) {
            if (pos->a == 0xff) continue;
            const auto &bg = bg_choice[((x / pwidth) + y_pattern_pos) % 2];
            *pos           = LinearColor(*pos).AlphaBlend(bg).repack();
        }
    }
}

}  // namespace timg