1// Aseprite
2// Copyright (c) 2020 Igara Studio S.A.
3// Copyright (c) 2001-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/util/conversion_to_surface.h"
13
14#include "base/24bits.h"
15#include "doc/algo.h"
16#include "doc/color_scales.h"
17#include "doc/image_impl.h"
18#include "doc/palette.h"
19#include "doc/rgbmap.h"
20#include "os/surface.h"
21#include "os/surface_format.h"
22
23#include <algorithm>
24#include <stdexcept>
25
26namespace app {
27
28using namespace doc;
29
30namespace {
31
32template<typename ImageTraits, os::SurfaceFormat format>
33uint32_t convert_color_to_surface(color_t color, const Palette* palette, const os::SurfaceFormatData* fd) {
34 static_assert(false && sizeof(ImageTraits), "Invalid color conversion");
35 return 0;
36}
37
38template<>
39uint32_t convert_color_to_surface<RgbTraits, os::kRgbaSurfaceFormat>(color_t c, const Palette* palette, const os::SurfaceFormatData* fd) {
40 return
41 ((rgba_getr(c) << fd->redShift ) & fd->redMask ) |
42 ((rgba_getg(c) << fd->greenShift) & fd->greenMask) |
43 ((rgba_getb(c) << fd->blueShift ) & fd->blueMask ) |
44 ((rgba_geta(c) << fd->alphaShift) & fd->alphaMask);
45}
46
47template<>
48uint32_t convert_color_to_surface<GrayscaleTraits, os::kRgbaSurfaceFormat>(color_t c, const Palette* palette, const os::SurfaceFormatData* fd) {
49 return
50 ((graya_getv(c) << fd->redShift ) & fd->redMask ) |
51 ((graya_getv(c) << fd->greenShift) & fd->greenMask) |
52 ((graya_getv(c) << fd->blueShift ) & fd->blueMask ) |
53 ((graya_geta(c) << fd->alphaShift) & fd->alphaMask);
54}
55
56template<>
57uint32_t convert_color_to_surface<IndexedTraits, os::kRgbaSurfaceFormat>(color_t c0, const Palette* palette, const os::SurfaceFormatData* fd) {
58 color_t c = palette->getEntry(c0);
59 return
60 ((rgba_getr(c) << fd->redShift ) & fd->redMask ) |
61 ((rgba_getg(c) << fd->greenShift) & fd->greenMask) |
62 ((rgba_getb(c) << fd->blueShift ) & fd->blueMask ) |
63 ((rgba_geta(c) << fd->alphaShift) & fd->alphaMask);
64}
65
66template<>
67uint32_t convert_color_to_surface<BitmapTraits, os::kRgbaSurfaceFormat>(color_t c0, const Palette* palette, const os::SurfaceFormatData* fd) {
68 color_t c = palette->getEntry(c0);
69 return
70 ((rgba_getr(c) << fd->redShift ) & fd->redMask ) |
71 ((rgba_getg(c) << fd->greenShift) & fd->greenMask) |
72 ((rgba_getb(c) << fd->blueShift ) & fd->blueMask ) |
73 ((rgba_geta(c) << fd->alphaShift) & fd->alphaMask);
74}
75
76template<typename ImageTraits, typename AddressType>
77void convert_image_to_surface_templ(const Image* image, os::Surface* dst,
78 int src_x, int src_y, int dst_x, int dst_y, int w, int h, const Palette* palette, const os::SurfaceFormatData* fd)
79{
80 const LockImageBits<ImageTraits> bits(image, gfx::Rect(src_x, src_y, w, h));
81 typename LockImageBits<ImageTraits>::const_iterator src_it = bits.begin();
82#ifdef _DEBUG
83 typename LockImageBits<ImageTraits>::const_iterator src_end = bits.end();
84#endif
85
86 for (int v=0; v<h; ++v, ++dst_y) {
87 AddressType dst_address = AddressType(dst->getData(dst_x, dst_y));
88 for (int u=0; u<w; ++u) {
89 ASSERT(src_it != src_end);
90
91 *dst_address = convert_color_to_surface<ImageTraits, os::kRgbaSurfaceFormat>(*src_it, palette, fd);
92 ++dst_address;
93 ++src_it;
94 }
95 }
96}
97
98struct Address24bpp
99{
100 uint8_t* m_ptr;
101 Address24bpp(uint8_t* ptr) : m_ptr(ptr) { }
102 Address24bpp& operator++() { m_ptr += 3; return *this; }
103 Address24bpp& operator*() { return *this; }
104 Address24bpp& operator=(uint32_t c) {
105 base::write24bits(m_ptr, c);
106 return *this;
107 }
108};
109
110template<typename ImageTraits>
111void convert_image_to_surface_selector(const Image* image, os::Surface* surface,
112 int src_x, int src_y, int dst_x, int dst_y, int w, int h, const Palette* palette, const os::SurfaceFormatData* fd)
113{
114 switch (fd->bitsPerPixel) {
115
116 case 8:
117 convert_image_to_surface_templ<ImageTraits, uint8_t*>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, fd);
118 break;
119
120 case 15:
121 case 16:
122 convert_image_to_surface_templ<ImageTraits, uint16_t*>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, fd);
123 break;
124
125 case 24:
126 convert_image_to_surface_templ<ImageTraits, Address24bpp>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, fd);
127 break;
128
129 case 32:
130 convert_image_to_surface_templ<ImageTraits, uint32_t*>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, fd);
131 break;
132 }
133}
134
135} // anonymous namespace
136
137
138void convert_image_to_surface(
139 const doc::Image* image,
140 const doc::Palette* palette,
141 os::Surface* surface,
142 int src_x, int src_y,
143 int dst_x, int dst_y,
144 int w, int h)
145{
146 gfx::Rect srcBounds(src_x, src_y, w, h);
147 srcBounds = srcBounds.createIntersection(image->bounds());
148 if (srcBounds.isEmpty())
149 return;
150
151 src_x = srcBounds.x;
152 src_y = srcBounds.y;
153 w = srcBounds.w;
154 h = srcBounds.h;
155
156 gfx::Rect dstBounds(dst_x, dst_y, w, h);
157 dstBounds = dstBounds.createIntersection(surface->getClipBounds());
158 if (dstBounds.isEmpty())
159 return;
160
161 src_x += dstBounds.x - dst_x;
162 src_y += dstBounds.y - dst_y;
163 dst_x = dstBounds.x;
164 dst_y = dstBounds.y;
165 w = dstBounds.w;
166 h = dstBounds.h;
167
168 os::SurfaceLock lockDst(surface);
169 os::SurfaceFormatData fd;
170 surface->getFormat(&fd);
171
172 switch (image->pixelFormat()) {
173
174 case IMAGE_RGB:
175 // Fast path
176 if (gfx::ColorRShift == fd.redShift &&
177 gfx::ColorGShift == fd.greenShift &&
178 gfx::ColorBShift == fd.blueShift &&
179 gfx::ColorAShift == fd.alphaShift) {
180 for (int v=0; v<h; ++v, ++src_y, ++dst_y) {
181 uint8_t* src_address = image->getPixelAddress(src_x, src_y);
182 uint8_t* dst_address = surface->getData(dst_x, dst_y);
183 std::copy(src_address,
184 src_address + RgbTraits::bytes_per_pixel * w,
185 dst_address);
186 }
187 return;
188 }
189 convert_image_to_surface_selector<RgbTraits>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, &fd);
190 break;
191
192 case IMAGE_GRAYSCALE:
193 convert_image_to_surface_selector<GrayscaleTraits>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, &fd);
194 break;
195
196 case IMAGE_INDEXED:
197 convert_image_to_surface_selector<IndexedTraits>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, &fd);
198 break;
199
200 case IMAGE_BITMAP:
201 convert_image_to_surface_selector<BitmapTraits>(image, surface, src_x, src_y, dst_x, dst_y, w, h, palette, &fd);
202 break;
203
204 default:
205 ASSERT(false);
206 throw std::runtime_error("conversion not supported");
207 }
208}
209
210} // namespace app
211