| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #include "pw_display/display.h" |
| |
| #include <utility> |
| |
| #include "pw_assert/assert.h" |
| #include "pw_color/color.h" |
| #include "pw_framebuffer/framebuffer.h" |
| #include "pw_framebuffer_pool/framebuffer_pool.h" |
| #include "pw_status/try.h" |
| |
| using pw::color::color_rgb565_t; |
| using pw::framebuffer::Framebuffer; |
| |
| namespace pw::display { |
| |
| Display::Display(pw::display_driver::DisplayDriver& display_driver, |
| pw::geometry::Size<uint16_t> size, |
| pw::framebuffer_pool::FramebufferPool& framebuffer_pool) |
| : display_driver_(display_driver), |
| size_(size), |
| framebuffer_pool_(framebuffer_pool) {} |
| |
| Display::~Display() = default; |
| |
| #if DISPLAY_RESIZE |
| Status Display::UpdateNearestNeighbor(const Framebuffer& framebuffer) { |
| PW_ASSERT(framebuffer.is_valid()); |
| if (!framebuffer.size().width || !framebuffer.size().height) |
| return Status::Internal(); |
| |
| const int fb_last_row_idx = framebuffer.size().height - 1; |
| const int fb_last_col_idx = framebuffer.size().width - 1; |
| |
| constexpr int kResizeBufferNumPixels = 80; |
| color_rgb565_t resize_buffer[kResizeBufferNumPixels]; |
| |
| const color_rgb565_t* fbdata = |
| static_cast<const color_rgb565_t*>(framebuffer.data()); |
| constexpr int kBytesPerPixel = sizeof(color_rgb565_t); |
| const int num_src_row_pixels = framebuffer.row_bytes() / kBytesPerPixel; |
| |
| const int num_dst_rows = size_.height; |
| const int num_dst_cols = size_.width; |
| for (int dst_row_idx = 0; dst_row_idx < num_dst_rows; dst_row_idx++) { |
| int src_row_idx = dst_row_idx * fb_last_row_idx / (num_dst_rows - 1); |
| PW_ASSERT(src_row_idx >= 0); |
| PW_ASSERT(src_row_idx < framebuffer.size().height); |
| int next_buff_idx = 0; |
| int dst_col_write_idx = 0; |
| for (int dst_col_idx = 0; dst_col_idx < num_dst_cols; dst_col_idx++) { |
| int src_col_idx = dst_col_idx * fb_last_col_idx / (num_dst_cols - 1); |
| PW_ASSERT(src_col_idx >= 0); |
| PW_ASSERT(src_col_idx < framebuffer.size().width); |
| int src_pixel_idx = src_row_idx * num_src_row_pixels + src_col_idx; |
| resize_buffer[next_buff_idx++] = fbdata[src_pixel_idx]; |
| if (next_buff_idx == kResizeBufferNumPixels) { |
| // Buffer is full, flush it. |
| PW_TRY(display_driver_.WriteRow( |
| span(resize_buffer, kResizeBufferNumPixels), |
| dst_row_idx, |
| dst_col_write_idx)); |
| next_buff_idx = 0; |
| dst_col_write_idx += kResizeBufferNumPixels; |
| } |
| } |
| |
| if (next_buff_idx) { |
| // Pixels in buffer, flush them. |
| PW_TRY(display_driver_.WriteRow( |
| span(resize_buffer, next_buff_idx), dst_row_idx, dst_col_write_idx)); |
| } |
| } |
| return OkStatus(); |
| } |
| #endif // DISPLAY_RESIZE |
| |
| Framebuffer Display::GetFramebuffer() { |
| return framebuffer_pool_.GetFramebuffer(); |
| } |
| |
| // This may be async |
| Status Display::ReleaseFramebuffer(Framebuffer framebuffer) { |
| pw::framebuffer_pool::FramebufferPool& fb_pool = framebuffer_pool_; |
| auto write_cb = [&fb_pool](pw::framebuffer::Framebuffer fb, Status status) { |
| PW_ASSERT_OK(status); |
| Status release_status = fb_pool.ReleaseFramebuffer(std::move(fb)); |
| PW_ASSERT_OK(release_status); |
| }; |
| if (!framebuffer.is_valid()) |
| return Status::InvalidArgument(); |
| if (framebuffer.size() != size_) { |
| #if DISPLAY_RESIZE |
| if (display_driver_.SupportsResize()) { |
| display_driver_.WriteFramebuffer(std::move(framebuffer), write_cb); |
| return OkStatus(); |
| } else { |
| Status result = UpdateNearestNeighbor(framebuffer); |
| write_cb(std::move(framebuffer), result); |
| return result; |
| } |
| #endif |
| // Rely on display driver's ability to support size mismatch. It is |
| // expected to return an error if it cannot. |
| } |
| |
| display_driver_.WriteFramebuffer(std::move(framebuffer), write_cb); |
| return OkStatus(); |
| } |
| |
| } // namespace pw::display |