mirror of
https://github.com/google/pebble.git
synced 2025-07-05 22:30:32 -04:00
165 lines
6.6 KiB
C
165 lines
6.6 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* 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
|
|
*
|
|
* http://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 "bitblt.h"
|
|
#include "bitblt_private.h"
|
|
|
|
#include "system/logging.h"
|
|
#include "util/math.h"
|
|
|
|
#include "util/bitset.h"
|
|
|
|
void bitblt_into_1bit_setup_compositing_mode(GCompOp *compositing_mode,
|
|
GColor tint_color) {
|
|
if ((*compositing_mode == GCompOpTint) || (*compositing_mode == GCompOpTintLuminance)) {
|
|
// Force our interpretation of the tint color to be black, white, or clear
|
|
tint_color = gcolor_get_bw(tint_color);
|
|
if (gcolor_equal(tint_color, GColorBlack)) {
|
|
*compositing_mode = GCompOpAnd;
|
|
} else if (gcolor_equal(tint_color, GColorWhite)) {
|
|
*compositing_mode = GCompOpSet;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bitblt_bitmap_into_bitmap_tiled_1bit_to_1bit(GBitmap* dest_bitmap,
|
|
const GBitmap* src_bitmap, GRect dest_rect,
|
|
GPoint src_origin_offset,
|
|
GCompOp compositing_mode,
|
|
GColor tint_color) {
|
|
bitblt_into_1bit_setup_compositing_mode(&compositing_mode, tint_color);
|
|
|
|
const int8_t dest_begin_x = (dest_rect.origin.x / 32);
|
|
const uint32_t * const dest_block_x_begin = ((uint32_t*)dest_bitmap->addr) + dest_begin_x;
|
|
const int dest_row_length_words = (dest_bitmap->row_size_bytes / 4);
|
|
// The number of bits between the beginning of dest_block and the beginning of the nearest 32-bit block:
|
|
const uint8_t dest_shift_at_line_begin = (dest_rect.origin.x % 32);
|
|
|
|
const uint32_t * const src_block_x_begin =
|
|
((uint32_t*)src_bitmap->addr) +
|
|
((src_bitmap->bounds.origin.x + (src_origin_offset.x % src_bitmap->bounds.size.w)) / 32);
|
|
const int src_row_length_words = (src_bitmap->row_size_bytes / 4);
|
|
const uint8_t src_shift_at_line_begin = ((src_bitmap->bounds.origin.x + src_origin_offset.x) % 32);
|
|
const uint8_t src_bits_left_at_line_begin = MIN(32 - src_shift_at_line_begin,
|
|
MIN(dest_rect.size.w + src_origin_offset.x,
|
|
src_bitmap->bounds.size.w));
|
|
|
|
// how many 32-bit blocks do we need to bitblt on this row:
|
|
const int16_t dest_end_x = (dest_rect.origin.x + dest_rect.size.w);
|
|
const uint8_t num_dest_blocks_per_row = (dest_end_x / 32) + ((dest_end_x % 32) ? 1 : 0) - dest_begin_x;
|
|
|
|
// The bitblt loops:
|
|
const int16_t dest_y_end = dest_rect.origin.y + dest_rect.size.h;
|
|
int16_t src_y = src_bitmap->bounds.origin.y + src_origin_offset.y;
|
|
for (int16_t dest_y = dest_rect.origin.y; dest_y != dest_y_end; ++dest_y, ++src_y) {
|
|
// Wrap-around source bitmap vertically:
|
|
if (src_y >= src_bitmap->bounds.origin.y + src_bitmap->bounds.size.h) {
|
|
src_y = src_bitmap->bounds.origin.y;
|
|
}
|
|
|
|
int8_t src_dest_shift = 32 + dest_shift_at_line_begin - src_shift_at_line_begin;
|
|
uint8_t dest_shift = dest_shift_at_line_begin;
|
|
uint8_t row_bits_left = dest_rect.size.w;
|
|
uint32_t *dest_block = (uint32_t *)dest_block_x_begin + (dest_y * dest_row_length_words);
|
|
uint32_t * const src_block_begin = (uint32_t *)src_block_x_begin + (src_y * src_row_length_words);
|
|
uint32_t *src_block = src_block_begin;
|
|
uint32_t src = *src_block;
|
|
rotl32(src, src_dest_shift);
|
|
uint8_t src_bits_left = src_bits_left_at_line_begin;
|
|
|
|
const uint32_t *dest_block_end = dest_block + num_dest_blocks_per_row;
|
|
const uint32_t *src_block_end = src_block + src_row_length_words;
|
|
|
|
while (dest_block != dest_block_end) {
|
|
|
|
const uint8_t number_of_bits = MIN(32 - dest_shift, MIN(row_bits_left, src_bits_left));
|
|
const uint32_t mask_outer_bit = ((number_of_bits < 31) ? (1 << number_of_bits) : 0);
|
|
const uint32_t mask = ((mask_outer_bit - 1) << dest_shift);
|
|
|
|
switch (compositing_mode) {
|
|
case GCompOpClear:
|
|
*(dest_block) &= ~(mask & src);
|
|
break;
|
|
|
|
case GCompOpSet:
|
|
*(dest_block) |= mask & ~src;
|
|
break;
|
|
|
|
case GCompOpOr:
|
|
*(dest_block) |= mask & src;
|
|
break;
|
|
|
|
case GCompOpAnd:
|
|
*(dest_block) &= ~mask | src;
|
|
break;
|
|
|
|
case GCompOpAssignInverted:
|
|
*(dest_block) ^= mask & (~src ^ *(dest_block));
|
|
break;
|
|
|
|
default:
|
|
case GCompOpAssign:
|
|
// this basically does: masked(dest_bits) = masked(src_bits)
|
|
*(dest_block) ^= mask & (src ^ *(dest_block));
|
|
break;
|
|
}
|
|
|
|
dest_shift = (dest_shift + number_of_bits) % 32;
|
|
row_bits_left -= number_of_bits;
|
|
src_bits_left -= number_of_bits;
|
|
|
|
if (src_bits_left == 0 && row_bits_left != 0) {
|
|
++src_block;
|
|
if (src_block == src_block_end) {
|
|
// Wrap-around source bitmap horizontally:
|
|
src_block = src_block_begin;
|
|
src_bits_left = src_bits_left_at_line_begin;
|
|
src_dest_shift = (src_dest_shift + src_bitmap->bounds.size.w) % 32;
|
|
} else {
|
|
src_bits_left = 32; // excessive right edge bits will be masked off eventually
|
|
}
|
|
src = *src_block;
|
|
rotl32(src, src_dest_shift);
|
|
if (dest_shift) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Proceed to next dest_block:
|
|
++dest_block;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bitblt_bitmap_into_bitmap(GBitmap* dest_bitmap, const GBitmap* src_bitmap,
|
|
GPoint dest_offset, GCompOp compositing_mode, GColor8 tint_color) {
|
|
GRect dest_rect = { dest_offset, src_bitmap->bounds.size };
|
|
grect_clip(&dest_rect, &dest_bitmap->bounds);
|
|
|
|
GBitmap src_clipped_bitmap = *src_bitmap;
|
|
src_clipped_bitmap.bounds.origin = (GPoint) {
|
|
src_bitmap->bounds.origin.x + (dest_rect.origin.x - dest_offset.x),
|
|
src_bitmap->bounds.origin.y + (dest_rect.origin.y - dest_offset.y)
|
|
};
|
|
|
|
bitblt_bitmap_into_bitmap_tiled(dest_bitmap, &src_clipped_bitmap, dest_rect,
|
|
GPointZero, compositing_mode, tint_color);
|
|
}
|
|
|
|
bool bitblt_compositing_mode_is_noop(GCompOp compositing_mode, GColor tint_color) {
|
|
return (((compositing_mode == GCompOpTint) || (compositing_mode == GCompOpTintLuminance)) &&
|
|
gcolor_is_invisible(tint_color));
|
|
}
|