blob: 3aaf764b3e29c01681180d041a67e4bfa45b14cc [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* 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 "glimp/gles/convert_pixel_data.h"
#include "starboard/log.h"
#include "starboard/memory.h"
namespace glimp {
namespace gles {
namespace {
// Helper method to properly copy pixel data from a source to destination
// pixel data, taking into account source and destination pitch.
void CopyPixelData(uint8_t* destination,
int destination_pitch,
const uint8_t* source,
int source_pitch,
int bytes_per_row,
int num_rows) {
if (destination_pitch == source_pitch) {
// If the pitches are equal, we can do the entire copy in one memcpy().
SbMemoryCopy(destination, source, destination_pitch * num_rows);
} else {
// If the pitches are not equal, we must memcpy each row separately.
for (int i = 0; i < num_rows; ++i) {
SbMemoryCopy(destination + i * destination_pitch,
source + i * source_pitch, bytes_per_row);
}
}
}
typedef void (*ConvertRowFunction)(int, uint8_t*, const uint8_t*, int);
// Remaps input pixel channels such that 4-byte destination color values will
// have byte X set from source color value's channel_X_source byte. If
// any value for channel_X_source is set to -1, a 0 is instead set for that
// destination channel.
template <int channel_0_source,
int channel_1_source,
int channel_2_source,
int channel_3_source>
void RemapPixelChannels(int source_bytes_per_pixel,
uint8_t* destination,
const uint8_t* source,
int num_pixels) {
for (int i = 0; i < num_pixels; ++i) {
uint8_t channel_0 = channel_0_source == -1 ? 0 : source[channel_0_source];
uint8_t channel_1 = channel_1_source == -1 ? 0 : source[channel_1_source];
uint8_t channel_2 = channel_2_source == -1 ? 0 : source[channel_2_source];
uint8_t channel_3 = channel_3_source == -1 ? 0 : source[channel_3_source];
destination[0] = channel_0;
destination[1] = channel_1;
destination[2] = channel_2;
destination[3] = channel_3;
destination += 4;
source += source_bytes_per_pixel;
}
}
// Given a destination and source format, returns a function that will convert
// a row of pixels in a source buffer to a row of pixels in a destination
// buffer that contains a different format. This function may not implement
// every conversion, it is expected that conversion implementations will be
// added as they are needed.
ConvertRowFunction SelectConvertRowFunction(PixelFormat destination_format,
PixelFormat source_format) {
if (destination_format == source_format) {
return &RemapPixelChannels<0, 1, 2, 3>;
} else if (destination_format == kPixelFormatRGBA8 &&
source_format == kPixelFormatARGB8) {
return &RemapPixelChannels<1, 2, 3, 0>;
} else if (destination_format == kPixelFormatRGBA8 &&
source_format == kPixelFormatBGRA8) {
return &RemapPixelChannels<2, 1, 0, 3>;
}
// Only what is currently needed by dependent libraries is supported, so
// feel free to add support for more pixel formats here as the need arises.
return NULL;
}
} // namespace
void ConvertPixelDataInplace(uint8_t* pixels,
int pitch_in_bytes,
PixelFormat destination_format,
PixelFormat source_format,
int width,
int height) {
if (destination_format == source_format) {
return;
}
SB_DCHECK(BytesPerPixel(destination_format) == BytesPerPixel(source_format));
// The destination format is different from the source format, so we must
// perform a conversion between pixels.
// First select the function that will reformat the pixels, based on
// the destination and source pixel formats.
ConvertRowFunction convert_row_function =
SelectConvertRowFunction(destination_format, source_format);
SB_DCHECK(convert_row_function)
<< "The requested pixel conversion is not yet implemented.";
// Now, iterate through each row running the selected conversion function on
// each one.
uint8_t* pixel_row = pixels;
for (int row = 0; row < height; ++row) {
convert_row_function(BytesPerPixel(source_format), pixel_row, pixel_row,
width);
pixel_row += pitch_in_bytes;
}
}
void ConvertPixelData(uint8_t* destination,
int destination_pitch_in_bytes,
PixelFormat destination_format,
const uint8_t* source,
int source_pitch_in_bytes,
PixelFormat source_format,
int width,
int height,
bool flip_y) {
if (destination_format == source_format && !flip_y) {
CopyPixelData(destination, destination_pitch_in_bytes, source,
source_pitch_in_bytes, BytesPerPixel(source_format) * width,
height);
} else {
// The destination format is different from the source format, so we must
// perform a conversion between pixels.
// First select the function that will reformat the pixels, based on
// the destination and source pixel formats.
ConvertRowFunction convert_row_function =
SelectConvertRowFunction(destination_format, source_format);
SB_DCHECK(convert_row_function)
<< "The requested pixel conversion is not yet implemented.";
// Now, iterate through each row running the selected conversion function on
// each one.
for (int dest_row = 0; dest_row < height; ++dest_row) {
int source_row = flip_y ? height - dest_row - 1 : dest_row;
convert_row_function(BytesPerPixel(source_format),
destination + dest_row * destination_pitch_in_bytes,
source + source_row * source_pitch_in_bytes, width);
}
}
}
} // namespace gles
} // namespace glimp